web-dev-qa-db-ja.com

「生の」文字列をgrepに渡す簡単な方法はありますか?

いくつかの文字はリテラルとして扱われないようにエスケープする必要があるため、grepにコマンドラインから使用する場合、「生の」文字列を供給することはできません。例えば:

$ grep '(hello|bye)' # WON'T MATCH 'hello'
$ grep '\(hello\|bye\)' # GOOD, BUT QUICKLY BECOMES UNREADABLE

私はprintfを使用して文字列を自動エスケープしていました:

$ printf '%q' '(some|group)\n'
\(some\|group\)\\n

これにより、文字列のbashエスケープバージョンが生成され、バックティックを使用して、これをgrep呼び出しに簡単に渡すことができます。

$ grep `printf '%q' '(a|b|c)'`

ただし、これは明らかにこの目的ではありません。出力内の一部の文字はエスケープされず、一部は不要にエスケープされます。例えば:

$ printf '%q' '(^#)'
\(\^#\)

^文字は、grepに渡すときにエスケープしないでください。

生の文字列を受け取り、grepでパターンとして直接使用できるbashエスケープバージョンの文字列を返すcliツールはありますか?どうすれば純粋なbashでこれを達成できますか?

49
slezica

拡張正規表現構文を使用するためにgrepを取得しようとしている場合、その方法はgrep -E(別名egrep)を使用することです。また、grep -F(別名fgrep)、およびGNU Coreutils、grep -P。]の新しいバージョンについても知っておく必要があります。

背景:元のgrepには、正規表現演算子のかなり小さなセットがありました。 Ken Thompsonの元の正規表現の実装でした。拡張されたレパートリーを備えた新しいバージョンは後で開発され、互換性の理由から別の名前が付けられました。 GNU grepの場合、grepとして呼び出された場合は従来の基本的なRE構文を理解し、egrepegrepの一部の構成体は、バックスラッシュエスケープを使用して特別な意味を導入することにより、grepで使用できます。

その後、Perlプログラミング言語は形式をさらに拡張しました。この正規表現の方言は、ほとんどの新参者が誤ってgrepもサポートすると期待しているようです。 grep -Pで、そうします。しかし、これはすべてのプラットフォームでまだ広くサポートされていません。

そのため、grepでは、次の文字に特別な意味があります:^$[]*.\

egrepでは、次の文字にも特別な意味があります:()|+?{}。 (繰り返しの中括弧は元のegrepにはありませんでした。)グループ化括弧は、\1\2などによる後方参照も可能にします。

grepの多くのバージョンでは、egrepスペシャルの前にバックスラッシュを置くことでegrepの動作を得ることができます。 \<\>のような特別なシーケンスもあります。

Perlでは、\w\s\dのような追加のエスケープが多数導入されました。 Perl 5では、正規表現機能が大幅に拡張され、貪欲でないマッチング*?+?など、非グループ化括弧(?:...)、lookaheads、lookbehindsなどが追加されました。

...とは言っても、本当にegrep正規表現をgrep正規表現に変換したい場合は外部プロセスを呼び出さずにを試してください${regex/pattern/substitution}egrep特殊文字ごとに;ただし、これは文字クラス、否定された文字クラス、またはバックスラッシュエスケープを正しく処理しないことを認識してください。

23
tripleee

正確な文字列を検索する場合は、

grep -F '(some|group)\n' ...

-Fは、grepに、正規表現として解釈せずに、パターンをそのまま処理するように指示します。

(これはfgrepとしてもしばしば利用可能です。)

43
ephemient

ユーザー指定の文字列でgrep -Eを使用すると、これでエスケープされます

ere_quote() {
    sed 's/[]\.|$(){}?+*^]/\\&/g' <<< "$*"
}

実行例

ere_quote ' \ $ [ ] ( ) { } | ^ . ? + *'
# output
# \\ \$ \[ \] \( \) \{ \} \| \^ \. \? \+ \*

これにより、正規表現に引用符付き文字列を安全に挿入できます。

例えばユーザーコンテンツから始まる各行を検索したい場合、ユーザーは。*として面白い文字列を提供します

userdata=".*"
grep -E -- "^$(ere_quote "$userdata")" <<< ".*hello"
# if you have colors in grep you'll see only ".*" in red
20
Riccardo Galli

以前の回答は、ダッシュ(-)で始まる1つの重要なこと、つまり文字列を見逃しているため完全ではないと思います。したがって、これはしません動作します:

echo "A-B-C" | grep -F "-B-"

これは:

echo "A-B-C" | grep -F -- "-B-"
3
LLL