rcmdnk's blog

Grep Pocket Reference (Pocket Reference (OReilly))

grepを使った時にちょっとひっかかったことがあったのでそれについてのメモ。

ドル記号($)

通常grepでメタ文字を検索するときにはバックスラッシュ(\)(または円マーク) を付けてエスケープする必要がありますが、 この際にシェルのエスケープ/変換も効くのでシェルで変換されるものに関しては 注意が必要です(他のシェルでも大概同じだと思いますがbashでの環境を前提にしてます)。

ドル記号($)は変数の参照に使うので、エスケープせずに 単純にこれを書くと続く文字列の変数を 参照する様な形になって、大概の場合は未定義変数=空文字で 全ての行が該当してしまいます1

$ printf "aaa\nbbb\$ccc" |grep  "$ccc"
aaa
bbb$ccc

これをきちんと$cccで検索するにはドル記号をエスケープして

$ printf "aaa\nbbb\$ccc" |grep  "\$ccc"
bbb$ccc

とするか、シングルクォートで囲むことで囲み内を展開しない様に

$ printf "aaa\nbbb\$ccc" |grep  '$ccc'
bbb$ccc

とすればOK2

$ printf "aaa\nbbb\$ccc" |grep  '\$ccc'
bbb$ccc

の様にバックスラッシュをを付けておいても(多分)grepでの展開時に\$が外れるので $cccで検索されます。

ただ、ドル記号単独の場合がちょっと特殊で、

$ printf "aaa\nbbb\$ccc" |grep  "\$"
aaa
bbb$ccc

の様に単なる空文字として認識されてる様子。 ただ、bashでは$単独ではそのままの文字として認識されるのと、

$ printf "aaa\nbbb\$ccc" |grep  \$
aaa
bbb$ccc

の様にダブルクォートを外しても同じ結果でエラーが出ないのでちょっと特殊。 上の$cccの場合だと、ダブルクォートを外すと 未定義で何も無いのと同じなので、grepを引数なしで実行しようとしてエラーが出ます。

なので空の文字""として認識されている様子。

このドル記号単独を検索したい場合は

$ printf "aaa\nbbb\$ccc" |grep  "\\$"
bbb$ccc

の様にバックスラッシュを2つ付ける(ダブルクォートなしでも大丈夫) か

$ printf "aaa\nbbb\$ccc" |grep  '\$'
bbb$ccc

シングルクォートで囲ってかつバックスラッシュでエスケープすると出来ます。

上の場合は恐らくシェル的に最初のバックスラッシュ二つの部分が、 前のバックスラッシュが後者をエスケープする形で一つ残り、 grep的に\$$だと理解してるんだと思います。 後者はそのまま\$grepに渡されて最終的に$として理解される感じ。

バックスラッシュ(\)

バックスラッシュを検索したい場合は

$ printf "aaa\nbbb\\ccc" |grep  "\\\\"
bbb\ccc

と四つのバックスラッシュを使う(ダブルクォートで囲むか何も囲まない)か

シングルクォートで囲って二つのバックスラッシュ

$ printf "aaa\nbbb\\ccc" |grep  '\\'
bbb\ccc

にすると出来ます。

これも前者の場合はまずシェルがバックスラッシュ二つ続きを一つに変換するので 結果的に後者と同じくバックスラッシュ二つになって、 これがgrepに渡されて前のバックスラッシュがエスケープになり 後ろのバックスラッシュが最終的に渡される感じかと。

これ以外だと、ダブルクォートの中にバックスラッシュ一つだと

$ printf "aaa\nbbb\\ccc" |grep  "\"
>

となります。二つ目のバックスラッシュがエスケープされるので ダブルクォートが閉じてないので入力が続いてる状態。

ダブルクォートにバックスラッシュ二つだと、

$ printf "aaa\nbbb\\ccc" |grep  "\\"
grep: trailing backslash (\)

の様に、grepに渡されるのがバックスラッシュ一つになってgrepが困惑してしまいます。 これはシングルクォート内にバックスラッシュ一つ書いても同じ。

まとめ

  • ドル記号の検索:
    • grep "\\$" または grep '\$'
  • バックスラッシュの検索:
    • grep "\\\\" または grep '\\'
Sponsored Links
  1. スクリプトの最初で

    #!/bin/sh -u
    

    としておいたりset -uしておけば未定義変数に関してエラーを吐かせる事も出来るので、 こういうミスしないためには未定義変数を使う場合はエラー、 としておくと無くせます。ですが、未定義変数は割りbashとかでは 当たり前に使いますし、NULL(““)か未定義かできちんと分けて判断することも 出来たりして敢えて使うこともあるので常に-uオプションを使うのは微妙な所かもしれません。

  2. bashではダブルクォート"で囲まれた文字列内では囲わない場合と同様変数を展開しますが、 シングルクォート'で囲まれた中では展開しません。

Sponsored Links

« Mavericksでのキーボード設定 Macでのマウス/トラックパッドの軌跡の加速度/速度の調整 »

}