web-dev-qa-db-ja.com

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

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

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

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

24
Binoy Babu

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

データがこの「風変わりな」カテゴリに該当する場合は、preおよびpostフィルタリングを少し行うだけで対応できます。以下に示すフィルターでは、\x01\x02\x03\x04の文字がデータのどこにも表示されないようにする必要があります。

以下は、単純なawkフィールドダンプにラップされたフィルターです。

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

更新; 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 filterであり、コメントで展開されています。
post filter\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
'
7
Peter.O

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

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

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

列を削除する

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

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

実際にsedを使用する必要がある場合は、最初のn-1フィールド、nthフィールド、および残りに一致する正規表現を記述し、nth(ここでnは2なので、最初のグループが一致します1時間:\{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のforループでは、次のようになります(ここでも、末尾のコンマは無視されます)。

$ 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ソリューションは他のソリューションから自然に続くと思いますが、途方もなく長くなり始めます

15
Michael Mrozek

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形式の出力

12
Panther

次の形式のスペース区切りファイルを考えます。

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は一般にfieldsの概念を扱うのに非常に優れています。スペースで区切られたファイルではなくCSVを処理する場合は、次のように使用できます。

awk -F,

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

ソース #3

5
tcdyl

これは削除に役立ちます

awk '{$2="";$0=$0;$1=$1}1'

入力

a b c d

出力

a c d
0
Steven Penny