シェルスクリプトで可変長データを読み取る
はじめに
こんなデータに出くわした。data.txtとする。
1, hoge, foo, bar, piyo, [ID: 1; a; b; c; d;ID: 2; a; b; c; d;ID: 3; a; b; c; d;ID: 4; a; b; c; d;ID: 5; a; b; c; d;] 2, hoge, foo, bar, piyo, [ID: 1; a; b; c; d;] 3, hoge, foo, bar, piyo, [ID: 1; a; b; c; d;ID: 2; a; b; c; d;ID: 3; a; b; c; d;] 4, hoge, foo, bar, piyo, [ID: 1; a; b; c; d;ID: 2; a; b; c; d;]
[]で囲まれた部分が可変長のデータになっている。可変長部分から'b'だけを取り出して、縦1列に並べたい。
1 b 2 b 3 b 4 b 5 b 1 b 1 b 2 b 3 b 1 b 2 b
今回一回限りのデータ処理だったので、久々にシェルで遊ぶことにした。以下は考えた順番通りのメモ。もっといい方法はあると思うが。
実践
下ごしらえとしてawkで[ ]の部分だけを取り出す。[ ]の中と外でセパレータが違うのでありがたい。
簡単のため、最初の1行だけで処理を考える。
head -1 data.txt | awk -F ',' '{print $6}'
[ID: 1; a; b; c; d;ID: 2; a; b; c; d;ID: 3; a; b; c; d;ID: 4; a; b; c; d;ID: 5; a; b; c; d;]
awkでforを回すことも考えた。しかし今回は先に試した別の方法がうまく機能した。
head -1 awk -F, '{print $6}' | tr "ID:" "\nID:" | awk -F ';' 'NR>1{print $3}'
'ID'を検索して、'ID'が見つかるたびに改行する。すると
[ ID 1; a; b; c; d; ID 2; a; b; c; d; ID 3; a; b; c; d; ID 4; a; b; c; d; ID 5; a; b; c; d;]
と複数の行であらわされた!あとは各行について、awkで'b'を取り出すだけだ。1行目は'['が邪魔なのでNR>1で回避しておく。
最後に、元データの各行についてこれを実行すればいいから、head -1 の部分をwhile readに変更して1行ずつ読み取るようにした。
cat data.txt | while read LINE; do echo $LINE | awk -F, '{print $6}' | tr "ID:" "\nID:" | awk -F ';' 'NR>1{print $3}' | cat -n; done
1 b 2 b 3 b 4 b 5 b 1 b 1 b 2 b 3 b 1 b 2 b
ついでに
一応関数化してみる。'cat -' で標準入力を受けられるものにした。
read_multi_frame(){ cat - | awk -F, '{print $6}' | tr "ID:" "\nID:" | awk -F ';' 'NR>1{print $3}' }
cat data.txt | while read LINE; do echo $LINE | read_multi_frame | cat -n; done
以上。