動的列幅と空のフィールドを使用した出力の解析

動的列幅と空のフィールドを使用した出力の解析

ドライバー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

同様のツールを使用してこの出力を解析しようとしましたが、成功しませんでしawksed

問題は、サイズ列の空の「フィールド」と列の動的幅にあります。

この出力を解析する方法を知っている人はいますか?

答え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 *」を追加しました。
  • join次に、ヌル区切り文字を使用する文字列に渡して1つの文字列にまとめます。したがって、解凍された形式はすでに使用可能であり、//=演算子が関数であるdefined-orため再評価されません。
  • 動的に生成された解凍形式を使用して、ヘッダーを含むすべての行に適用を進めます。
  • unpack提供された形式を使用して文字列(この場合は現在の行)を解凍し、解凍されたフィールドをエクスポートします。
  • 次に、放出されたフィールドをmapここに入力して各フィールドを1つずつ処理し、{ ... }コードに記載されている手順を実行します。私たちの場合、各フィールドで次のことを行います。 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"

関連情報