以前シェルスクリプトで行数を調べる方法
について色々考えてみましたが、
その中のgrep -c ''
に関して、BSD系での罠があったのでそれについて。
シェルスクリプトで行数の数え方
シェルスクリプトで行数を数えるといえばまずは
wc
と言うコマンドがあります。
単にwc file
とするとファイルの行数、ワード数、文字数を表示してくれます。
-l
オプションを使えば行数だけ表示。(共に最後にファイル名も出ます。)
$ wc -l .bashrc
776 .bashrc
パイプを使ってコマンド結果を渡す事も出来るのですが、
$ printf "abc\nabc"|wc -l
1
の様に2行に渡る出力も1になっています。
これはwc
が最後の改行の数を数えているからで、上だと改行が一つなので1になります。
(prinf "abc"
なら0になります。)
ファイルに何か書いて保存すると、通常のエディタでは最後に改行コードを入れるので
wc
をファイルに適用すると直感的に正しいものが帰ってきますが、
上の様にコマンドの出力とかを数えようとすると間違えてしまうことがあります。
そこで、grep
の行を数える-c
オプションを使った方法がより直感的に出来ます。
上ではgrep -c ''
と、ゼロ文字がある行を数えよ、というコマンドを使ってますが、
これで全ての行が該当することになり出力が全行数になります。
この場合は行末に改行があろうがなかろうが関係ないので
$ printf "abc\nabc"|grep -c ''
2
$ printf "abc\nabc\n"|grep -c ''
2
の様な形で数えられます。
BSD系でのgrep -c ‘‘の罠
ただこれが何も出力が無いような(もしくは空のファイルのような)ものを 調べると問題が起きる事に気づきました。
GNUのgrep
だと
$ printf ""|grep -c ''
0
$
の様に0を返してくれるのですが、
MacとかのBSD系のgrep
だと、
$ printf ""|grep -c ''
$
と、空の場合には何も出力してくれません。
wc
の場合には
$ printf ""|wc -l
0
$
と当然両方共0を返してくれます。
対処法: grep -c ^
上にも書いた様にgrep
とwc
では数え方が違うので
どうしてもgrep
で数えた方が便利な場合があるわけですが、
その場合0行になる可能性があると問題が出るわけです。
BSD系で絶対に使わないスクリプトなら良いのですが、そうでない場合なんとかしないといけません。
このまま-c
を使うとするならば、
nlines=$(print "$value"|grep -c '')
if [ "$nlines" = "" ];then
nlines=0
fi
の様にしておけば取り敢えずは大丈夫です。
が、ちょっとスマートではないです。
そこで数える文字を替えて
$ printf ""|grep -c ^
0
としてあげるとBSDのgrep
でも0を出してくれます。
^
は行頭の表す正規表現です。
なので、同様に
$ printf ""|grep -c $
0
と、行末を表す$
を使ってもいけます。
まとめ
grep
等の基本的なコマンドでもGNUとBSDでちょっと違うところがあって時々間違いが起こりますが、
これは中々気付かないところでした。
ただ、grep
では該当行が無い場合、$?
で見れる返り値が1となってエラー終了するのですが、
BSDで
$ printf ""|grep -c ''
$ echo $?
0
$
とすると正常終了(0)になっています。
^
や$
でやるとエラー終了(1)になっているので
仕様というよりバグな感じがします。
ざっと探してみた感じこのことについて書いてあるものは見つからなかったので
その辺よく分かりませんが、''
の空文字検索はちょっと危険なので
取り敢えずは^
などを使った方が良い、ということです。