grep を特殊なファイル名でも動作させる

grep を特殊なファイル名でも動作させる

txt名前にスペースや などの特殊文字が含まれる可能性があるファイルのセットがあります#

持っているが持っていないファイルをすべてリストするgrep解決策があります。grep -L "cannot have" $(grep -l "must have" *.txt)must havecannot have

たとえば、 というabc defg.txt1 行のみを含むファイルがあるとしますmust have

したがって、通常は grep ソリューションで が見つかるはずですabc defg.txtが、次の結果が返されます。

grep: abc: No such file or directory
grep: defg.txt: No such file or directory

#を含むファイル名の場合、grep ソリューションも無効であると思います。

grep ソリューションを修正するのを手伝ってくれる人はいますか?

答え1

もしさらに遠くまで行きたい場合は、awk で 1 回のパスで実行できます。

awk 'function s(){if(a&&!b){print f}} FNR==1{s();f=FILENAME;a=b=0} 
  /must have/{a=1} /cannot have/{b=1} END{s()}' filepattern

最近の gawk では、BEGINFILE と ENDFILE を使って簡素化できます。(すべての awk の回答と同様に、awk コマンドを -f を使用してファイルに入れることができます。また、ほとんどの場合と同様に、必要に応じて簡単に perl に変換できます。)

答え2

すでに GNU 固有のオプション ( -L) を使用しているので、次のようにすることができます。

grep -lZ -- "must have" *.txt | xargs -r0 grep -L -- "cannot have"

アイデアは、 を使用して-ZNUL 区切りのファイル名のリストを印刷し、 を使用してxargs -r0そのリストを 2 番目の に引数として渡すことですgrep

コマンド置換は、デフォルトでは、スペース、タブ、改行 (および では NUL zsh) で分割されます。 以外の Bourne 系シェルではzsh、分割された各単語に対してグロブも実行されます。

次のようにすることができます:

IFS='
' # split on newline only
set -f # disable globbing
grep -L -- "cannot have" $(
    set +f # we need globbing for *.txt in this subshell though
    grep -l -- "must have" *.txt
  )

しかし、それでも改行文字を含むファイル名では動作が中断されます。

zsh(そして唯一)ではzsh、次のことが可能です。

IFS=$'\0'
grep -L -- "cannot have" $(grep -lZ -- "must have" *.txt)

または:

grep -L -- "cannot have" ${(ps:\0:)"$(grep -lZ -- "must have" *.txt)"}

答え3

代わりにを使用しfindgrepシェル コマンドを使用することを検討してください。

find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'

関連情報