巨大なファイルをシェルスクリプトで読む時
ちょっと2000万行くらいのcsvを読む機会があって、その時の小ネタ。
注釈追記)
コメントでも書いたんですけど、コマンド置換で cat
でファイル開いて for
に食わせるよりも、
1行ずつ処理するのは while read
の方が一般的みたいです。
なんか注釈追記したら非常に実の無い記事になってしまったw
中身は
こんな感じのが2000万行くらいずらずら続いてる感じ。
items.txt
"item_id","title","description","price","url","image_url"
"11111","たいとる","でぃすくりぷしょん","1000","http://example.com/item/11111","http://example.com/11111.jpg"
"22222","たいとる2","でぃすくりぷしょん2","2000","http://example.com/item/22222","http://example.com/22222.jpg"
1行ずつ処理するとき
駄目なやつ
コマンド置換
`cat items.txt`
$(cat items.txt)
だと、いったん全部ファイルの中身展開してから処理に流れるので、ちょっとまずい事になる。
これはコマンド置換のせいなので cat
に限らず head
とか tail
とかでも同じ。
駄目
for x in `cat items.txt`; do
echo $x
done;
大丈夫なやつ
read
で1行ずつ読むのが正解。
おk
while read x; do
echo $x
done < items.txt
これでもおk。
おk2
cat items.txt | while read x; do
echo $x
done
余談
コレを
- ダブルクォートを消して
- tsvに変換
したので、自分用メモも兼ねてついでにそれも。
bash
#!/bin/bash
# とりあえずヘッダはstderrに吐いておこうかな
head -n 1 items.txt >&2
while read x; do
echo $x | awk '
BEGIN {
FS = "," # 入力フィールドセパレータ
OFS = "\t" # 出力フィールドセパレータ
}
{
# 各フィールドを囲ってる前後のダブルクォートを除去
for(i=1; i<=NF; ++i) {
sub("^\"", "", $i);
sub("\"$", "", $i);
}
print $0 # OFS に ¥t 指定してるのでコレでおk
}'
done < <(tail -n +2 items.txt) # 1行目はヘッダなので除外