sedまたはawkでCSVファイルを操作する方法は?


23

sedまたはを使用してCSVファイルに次のことを行うにはどうすればよいawkですか?

  • 列を削除する
  • 列を複製する
  • 列を移動する

200行を超える大きなテーブルがありますが、にあまり詳しくありませんsed


1
CrossがAskUbuntuに投稿されました
enzotib

@enzotibリンクを投稿できますか?
n0pe

@MaxMackie askubuntu.com/questions/88142/...。私はこの時間にmodを手に入れることができないので、喜んで移行するかどうかを依頼するフラグを立てました。既に受け入れられた答えがあるので、彼らがそうするかどうかは
わかり

@MichaelMrozek、うーん、これらの状況で通常何が起こるのですか?単に複製を保持しますか?
n0pe

1
基本的なツールしか使用できないシステムで実行する必要がない限り、csvファイルを処理するための堅牢なコマンドラインツールはありますか?を
ジル「SO-悪であるのをやめる」

回答:


7

フィールドをカットして再配置する方法(他の回答で説明)とは別に、風変わりなCSVフィールドの問題があります。

データがこの「風変わりな」カテゴリに分類される場合、少しの事前および事後フィルタリングがそれを処理できます。次に示すフィルタは、文字を必要とし\x01\x02\x03\x04どこでもあなたのデータで表示されないようにします。

以下に、単純なawkフィールドダンプをラップするフィルターを示します。

注: field-fiveには無効または不完全な「引用されたフィールド」レイアウトがありますが、行の最後では無害です(CSVパーサーによって異なります)。しかし、もちろん、現在の行末位置から交換すると、問題のある予期しない結果が発生します。

更新; user121196は、コンマが末尾の引用符の前にある場合のバグを指摘しています。これが修正です。

データ

cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF

コード

sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
  awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
    sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g' 

出力:

field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five

"15111 N. Hayden Rd., Ste 160,"
""

以下は、コメントで展開されたpreフィルターです。ポストフィルタは、のちょうど逆です。、、
\x01\x02\x03\x04

sed -r '
    s/^/,/                # add a leading comma delimiter
    s/\\"/\x01/g          # obfuscate escaped quotation-mark (\")
    s/,"([^"]*)"/,\x02\1\x03/g    # obfuscate quotation-marks
    s/,"/,\x02/           # when no trailing quote on last field  
    :MC                   # obfuscate commas embedded in quotes
    s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
    tMC
    s/^,//                # remove spurious leading delimiter
'

このフィルターに基づいてn番目の列をどのように削除しますか?
user121196

@ user121196-最初の文で述べたように、この回答はCSVデータをより一貫性のあるものにする方法を示しています。一時的に引用符で埋め込まれたコンマをニュートラルトークン文字で置き換え、移動/カット/削除にカンマ戻します。繰り返しますが、前述のように、移動/カット/削除のステップは単純なawk field-dumpに置き換えられます。
Peter.O

1
それは、このような場合のために失敗した: "15111 N.ヘイデンRdを、スイート160、。"、 ""
user121196

@ user121196:それを指摘してくれてありがとう。回答を修正して更新しました。
Peter.O

15

これは、CSVファイルで区切り文字にのみコンマを使用するか、次のような狂気があるかどうかによって異なります。

フィールド1、「フィールド、2」、フィールド3

これは、単純なCSVファイルを使用していることを前提としています。

列の削除

1つの列をさまざまな方法で取り除くことができます。例として列2を使用しました。最も簡単な方法はおそらく使用cutすることです。これにより、区切り文字を指定したり、-d印刷するフィールドを指定したりできます-f。これは、コンマで分割し、フィールド1、およびフィールド3から最後まで出力するように指示します。

$ cut -d, -f1,3- /path/to/your/file

実際にを使用する必要がある場合はsed、最初のn-1フィールド、nthフィールド、および残りに一致する正規表現を記述し、出力のスキップを行うことができますnます(ここでnは2なので、最初のグループは1timeに一致します\{1\})。

$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file

これを行うには多くの方法があります awk特にエレガントな。forループを使用できますが、末尾のコンマを処理するのは苦痛です。次のようなものであることを無視します:

$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file

フィールド1を出力substrしてから、フィールド2以降のすべてを使用する方が簡単だと思います。

$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file

これはさらに列に迷惑です

列の複製

sed、この基本的に以前と同じ表現ですが、あなたはまた、ターゲット列をキャプチャし、交換でそのグループを複数回含まれます。

$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file

ではawkループ方法のためには、(再び末尾のカンマを無視して)のようなものになるだろう:

$ awk -F, '{
for(i=1; i<=NF; i++) {
    if(i == 2) printf "%s,", $i;
    printf "%s,", $i
}
print NL
}' /path/to/your/file

substr道:

$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file

(tcdylは彼の答えでより良い方法を思いついた)

列を移動する

私はsed解決策が他のものから自然に続くと思うが、それは途方もなく長くなり始める


それはロードされた答えです!+1 :)
ジャイパルシン

途方もなく長い?パー
ジル 'SO-悪であるのをやめる'

12

awkあなたの最善の策です。awk番号でフィールドを印刷するので...

awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file

印刷せずに列を削除するには:

 awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file

順序を変更するには:

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file

出力ファイルにリダイレクトします。

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file

awk 出力もフォーマットできます。

awk形式の出力


CSVなので、も必要BEGIN { FS=","; OFS=","; }です。

1
FS = OFS = "、"でも機能すると思います。

5

次の形式のスペース区切りファイルを指定します。

1 2 3 4 5

次のようにawkでフィールド2を削除できます。

awk '{ sub($2,""); print}' file

返す

1  3 4 5

必要に応じて、列2を列nに置き換えます。

列2を複製するには

awk '{ col = $2 " " $2; $2 = col; print }' file

返す

1 2 2 3 4 5

列2と3を切り替えるには

awk '{temp = $2; $2 = $3; $3 = temp; print}'

返す

1 3 2 4 5

awkは一般に、フィールドの概念を扱うのに非常に優れています。スペースで区切られたファイルではなく、CSVを処理している場合は、単に使用できます

awk -F,

フィールドをデフォルトのスペースではなくコンマとして定義します。オンラインには多くの優れたawkリソースがありますが、そのうちの1つを以下にソースとしてリストします。

#3のソース


私は多くのことは知らないawkが、それは、出力に思わフィールドセパレータであっても、スペースで区切られ,(それが入力をどのように処理するか、フィールド・セパレータは、単にコントロール)
マイケル・Mrozek

@MichaelMrozek:はい、OFS awk変数が出力フィールドセパレーターを制御します。
エンゾチブ

はい、そして私の答えで述べたように、区切り文字を変更するためにawkに-Fオプションを渡すことができます(たとえば-F、)
tcdyl

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.