シェルスクリプトのタブ文字と変数展開
先日、シェルスクリプトの動作でハマったので書き留めておく。
やりたいのはこういうことだ。
log.txt
aaa bbb cccddd eee fff ggg hhh
こういうlogファイルがあり、「aaa bbb」と「eee fff」をgrepしたかった。
ちなみに、「aaa bbb」、「eee fff」、「ggg hhh」の間にあるのはタブ文字だ。
タブ文字の入力の仕方
まず試しにターミナル上で、
$ grep aaa"\t"bbb log.txt
とやってみたが、引っかからない。
ターミナル上からタブ文字を入力するには、"\t"ではダメで、
$ grep aaa$'\t'bbb log.txt
のように $'\t' とやって、シェルの機能でタブ文字に置換してもらわないといけない。
もしくはターミナル上で「Ctrl+V, タブキー」を打つとタブ文字が入力できる。
$ grep 'aaa bbb' log.txt
タブ文字(あるいはスペース)の変数展開
現実は悲しいことにgrepするだけの処理だけではなくgrepしたあとawkで集計しメールするといったことをやる必要がある。
ただここでは話を簡単にするためにgrepするだけのシェルスクリプトを用意する。
そしてgrepする文字はあとで変更可能なように変数に入れておく。
grep.sh
#!/bin/bash INCLUDE='aaa bbb\|eee fff' grep $INCLUDE log.txt
grepでOR検索する際には「\|」で区切ると出来る。
さて、grep.shを実行する。zshだとちゃんと動くが、bashだと動かない。
bash-3.2$ /bin/sh ./grep.sh grep: bbb\|eee: No such file or directory grep: fff: No such file or directory log.txt:aaa bbb
エラーがおきている。なにがおきているのだろう。
「-x」オプションをつけて、動作をみてみよう。
bash-3.2$ /bin/sh -x ./grep.sh + INCLUDE='aaa bbb\|eee fff' + grep aaa 'bbb\|eee' fff log.txt grep: bbb\|eee: No such file or directory grep: fff: No such file or directory log.txt:aaa bbb
2行目がおかしい。本当は、「grep 'aaa bbb\|eee fff' log.txt」となってほしいが、そうなっていない。
ではどうすればいいか。
grep.sh の
grep $INCLUDE log.txt
を
grep "$INCLUDE" log.txt
に修正すればいい。
そうすればきちんと動く
bash-3.2$ /bin/sh -x ./grep.sh + INCLUDE='aaa bbb\|eee fff' + grep 'aaa bbb\|eee fff' log.txt aaa bbb eee fff
なんでこういう動作になるかというと、Bashでは展開する変数をクォートしなければ単語分割が起きるからだ。
以下の記事に詳しく書かれている。
http://qiita.com/uasi/items/82b7708d5da213ba7c31
まとめ
Rubyで書いたほうがはやい。