동적 열 너비와 빈 필드로 출력 구문 분석

동적 열 너비와 빈 필드로 출력 구문 분석

지드라이브list다음 예와 같은 파일 목록을 인쇄하는 하위 명령이 있습니다 .

gdrive list

산출:

Id                                  Name                      Type   Size     Created
1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5   info.pdf                  bin    10.0 B   2018-08-27 20:26:20
1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl   2018-12-ss-scalettapass   dir             2018-08-27 20:26:19

awk나는 성공 여부 에 관계없이 도구를 사용하여 이 출력을 구문 분석하려고 합니다 sed.

문제는 크기 열의 빈 '필드'와 열의 동적 너비입니다.

이 출력을 구문 분석하는 방법을 아는 사람이 있습니까?

답변1

awk는 고정 너비 데이터를 처리할 수 있습니다. 먼저 열 너비를 결정해야 합니다.

fieldwidths=$(head -n 1 file | grep -Po '\S+\s*' | awk '{printf "%d ", length($0)}')

이 값은 "36 26 7 9 7 "- 마지막 필드가 7자보다 큽니다. 임의로 70자로 만들어 보겠습니다.

fieldwidths=${fieldwidths/% /0}

이제 데이터를 읽고 이를 CSV로 변환해 보겠습니다.

awk -v FIELDWIDTHS="$fieldwidths" '{
    for (i=1; i<=NF; i++) {
        val = $i
        sub(/ *$/, "", val)
        gsub(/"/, "\"\"", val)
        printf "%s\"%s\"", (i==1 ? "" : ","), val
    }
    print ""
}' file

출력:

"Id","Name","Type","Size","Created"
"1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5","info.pdf","bin","10.0 B","2018-08-27 20:26:20"
"1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl","2018-12-ss-scalettapass","dir","","2018-08-27 20:26:19"

Perl과 동일한 기능

perl -lne '
    if ($. == 1) {
        @head = ( /(\S+\s*)/g );
        pop @head;
        $patt = "^";
        $patt .= "(.{" . length($_) . "})" for @head;
        $patt .= "(.*)\$";
    }
    print join ",", map {s/"/""/g; s/\s+$//; qq("$_")} (/$patt/o);
' file

답변2

헤더(첫 번째 줄)를 검사하여 동적으로 압축 풀기 템플릿을 생성하는 함수를 Perl사용하여 이를 수행할 수 있습니다 .unpack

perl -lpe '
    $fmt //= join "", map("A" . length(), /\H+\h+(?=\H)/g), "A*";
    $_ = join ",", map { s/"/""/gr =~ s/(.*)/"$1"/r } unpack $fmt;
' input-file.txt

설명:

  • -pperl줄 단위로 파일을 사용 하게 됩니다 . 레코드라고도 불리는 각 라인은 $_. 또 다른 효과는 -p다음 레코드를 가져오기 전에 현재 레코드를 자동 인쇄한다는 것입니다.
  • -l2가지 일을 하고, 세트를 합니다ORS = RS = \n
  • 정규식은 /\H+\h+(?=\H)/g마지막 필드를 제외한 모든 필드를 가져온 다음 에 공급합니다 map.
  • map이러한 필드의 길이를 계산하고 각 필드에 "A" 접두사를 붙입니다.
  • 위의 마지막 필드를 선택하지 않는 대신 포괄적인 "A*"를 추가합니다.
  • 그런 다음 joinNull 구분 기호를 사용하여 문자열로 묶는 전달됩니다. 따라서 압축 해제 형식은 사용할 준비가 되었으며 함수 //=인 연산자 로 인해 다시 계산되지 않습니다 defined-or.
  • 이제 동적으로 생성된 언팩 포맷으로 무장하여 헤더를 포함한 모든 라인에 적용을 진행합니다.
  • unpack제공된 형식을 사용하여 문자열(이 경우 현재 줄)의 압축을 풀고 압축이 풀린 필드를 내보냅니다.
  • 그런 다음 방출된 필드는 map하나씩 입력되어 { ... }코드에 설명된 단계를 수행합니다. 우리의 경우 각 필드에서 다음을 수행합니다. a) 큰따옴표를 두 배로 늘립니다. b) 필드를 큰따옴표로 묶습니다.
  • 필드 편집이 완료 되면 map해당 필드를 으로 전달하고 join쉼표를 사용하여 결합하여 ,멋진 작은 CSV파일을 만듭니다.
  • 추신:(ASCII의 경우 A) 형식 지정 문자를 사용할 때 unpack에 의해 생성된 필드의 후행 공백을 잘라낼 필요가 없었습니다 .unpackA

산출:

"Id","Name","Type","Size","Created"
"1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5","info.pdf","bin","10.0 B","2018-08-27 20:26:20"
"1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl","2018-12-ss-scalettapass","dir","","2018-08-27 20:26:19"

이 작업은 도구로 수행할 수 있지만 sed2단계 접근 방식이 필요합니다. 먼저 입력의 헤더 라인을 사용하여 스크립트를 sed동적으로 생성한 다음 입력 파일(헤더 포함)에 대해 작동하여 다음 작업을 수행합니다. 다음과 같이 원하는 작업을 수행합니다.

if="input-file.txt"
cmd=$(< "$if" head -n 1 | perl -lne 'print join $/, reverse map { $s += length();qq[s/./\\n/$s] } /\H+\h+(?=\H)/g')
sed -e '
    '"${cmd}"'
    s/"/""/g
    s/[[:blank:]]*\n/","/g
    s/.*/"&"/
' < "$if"

관련 정보