web-dev-qa-db-ja.com

awkに二重引用符内のフィールド区切り文字を無視させる方法は?

コンマ区切り値ファイルの2列を削除する必要があります。 csvファイルの次の行を検討してください。

"[email protected],www.example.com",field2,field3,field4
"[email protected]",field2,field3,field4

今、私が最後に欲しい結果:

"[email protected],www.example.com",field4
"[email protected]",field4

次のコマンドを使用しました。

awk 'BEGIN{FS=OFS=","}{print $1,$4}'

しかし、引用符の中にある埋め込まれたコンマは問題を引き起こしています、次は私が得ている結果です:

"[email protected],field3
"[email protected]",field4

ここで私の質問は、二重引用符内の「、」をawkに無視させる方法です。

24
Deepak K M

GNU awkマニュアル( http://www.gnu.org/software/gawk/manual/gawk.html#Splitting-By-Content )から:

$ awk -vFPAT='([^,]*)|("[^"]+")' -vOFS=, '{print $1,$4}' file
"[email protected],www.example.com",field4
"[email protected]",field4

フィールド内の改行などを含むより一般的なCSVの解析については、 awkを使用してCSVを効率的に解析する最も堅牢な方法は何ですか を参照してください。

36
Ed Morton

これはbash/awkソリューションではありませんが、 CSVKit をお勧めします。これは、pip install csvkitcsvcut など、CSVで特に動作するコマンドラインツールのコレクションを提供します。

csvcut --columns=1,4 <<EOF
"[email protected],www.example.com",field2,field3,field4
"[email protected]",field2,field3,field4
EOF

出力:

"[email protected],www.example.com",field4
[email protected],field4

不要な引用符は削除されますが、これは問題になりません。

CSVKitのドキュメントをお読みください ここではRTD 。 ThoughtBotには Nice little blog post があり、CSVKitについて学んだこのツールを紹介しています。

10
4ae1e1

サンプル入力ファイルでは、最初のフィールドおよび最初のフィールドのみであり、引用符で囲まれています。これが一般的に当てはまる場合、2番目と3番目の列を削除する方法として次を検討してください。

_$ awk -F, '{for (i=1;i<=NF;i++){printf "%s%s",(i>1)?",":"",$i; if ($i ~ /"$/)i=i+2};print""}' file
"[email protected],www.example.com",field4
"[email protected]",field4
_

コメントで述べたように、awkは引用符で区切られた区切り文字をネイティブに理解しません。この解決策は、引用符で終わる最初のフィールドを検索することで回避します。次に、後続の2つのフィールドをスキップします。

詳細

  • for (i=1;i<=NF;i++)

    これは、各フィールドforiを開始します。

  • printf "%s%s",(i>1)?",":"",$i

    これはフィールドiを出力します。最初のフィールドでない場合、フィールドの前にコンマが付きます。

  • if ($i ~ /"$/)i=i+2

    現在のフィールドが二重引用符で終わる場合、これはフィールドカウンターを2ずつ増やします。これは、フィールド2と3をスキップする方法です。

  • _print""_

    forループの処理が完了すると、改行が出力されます。

4
John1024

このawkは、引用符で囲まれたフィールドの場所に関係なく機能し、エスケープされた引用符でも機能します。

awk '{while(match($0,/"[^"]+",|([^,]+(,|$))/,a)){
      $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
      print b[1] b[4];x=0}' file

入力

"[email protected],www.example.com",field2,field3,field4  
"[email protected]",field2,field3,field4  
field1,"[email protected],www.example.com",field3,field4  

出力

"[email protected],www.example.com",field4
"[email protected]",field4
field1,field4

でも動作します

field1,"field,2","but this field has ""escaped"\" quotes",field4

強力なFPAT変数が失敗すること!


説明

 while(match($0,/"[^"]+",|([^,]+(,|$))/,a))

一致が成功する限り継続するwhileループを開始します(つまり、フィールドがあります)。
一致は、フィールドに偶然一致する正規表現の最初の出現と一致し、配列aに格納します

 $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]

セット$0は、一致したフィールドの末尾から開始し、一致したフィールドをbの対応する配列位置に追加します。

  print b[1] b[4];x=0}

bから必要なフィールドを出力し、次の行のxをゼロに戻します。


欠陥

フィールドにエスケープされた引用符とコンマの両方が含まれる場合は失敗します


編集

空のフィールドをサポートするように更新されました

awk '{while(match($0,/("[^"]+",|[^,]*,|([^,]+$))/,a)){
     $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
     print b[1] b[4];x=0}' file
1
user4453924