rcmdnk's blog

Amazon grep Pocket Reference: A Quick Pocket Reference for a Utility Every Unix User Needs (Pocket Reference (OReilly)) [Kindle edition] by John Bambenek, Agnieszka Klus Computer Science Kindleストア

以前シェルスクリプトで行数を調べる方法 について色々考えてみましたが、 その中の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 ^

上にも書いた様にgrepwcでは数え方が違うので どうしても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)になっているので 仕様というよりバグな感じがします。

ざっと探してみた感じこのことについて書いてあるものは見つからなかったので その辺よく分かりませんが、''の空文字検索はちょっと危険なので 取り敢えずは^などを使った方が良い、ということです。

Sponsored Links
Sponsored Links

« クレジットカード会社から不正利用の連絡がきた件 Firefox 53 リリース: タブバーなどをコンパクトに出来るデフォルトテーマが追加 »

}