동적 열 너비와 빈 필드를 사용하여 출력 구문 분석

동적 열 너비와 빈 필드를 사용하여 출력 구문 분석

운전사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

Perlunpack함수를 사용하여 헤더(첫 번째 줄)를 확인하여 압축 풀기 템플릿을 동적으로 생성하면 됩니다 .

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*"를 추가했습니다.
  • join그런 다음 널 구분 기호를 사용하는 문자열 에 전달하여 하나의 문자열로 묶습니다. 따라서 압축이 풀린 형식은 이미 사용 가능하며 //=연산자가 defined-or함수 이므로 다시 평가되지 않습니다 .
  • 이제 동적으로 생성된 압축 풀기 형식을 사용하여 헤더를 포함한 모든 행에 적용을 진행합니다.
  • unpack제공된 형식을 사용하여 문자열(이 경우 현재 줄)의 압축을 풀고 압축이 풀린 필드를 내보냅니다.
  • 그런 다음 방출된 필드를 map여기에 입력하여 각 필드를 하나씩 작업하고 { ... }코드에 설명된 단계를 수행합니다. 우리의 경우 각 필드에서 다음을 수행합니다. a) 큰따옴표. b) 필드를 큰따옴표로 묶습니다.
  • 필드를 편집한 후 map해당 필드를 입력 join하고 쉼표를 사용하여 연결하여 ,멋진 작은 파일을 만듭니다 CSV.
  • 첨부된:unpack(ASCII의 경우 A) 형식 문자를 사용할 unpack때 에서 생성된 필드의 후행 공백을 잘라낼 필요가 없습니다 .A

산출:

"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"

관련 정보