4

Linuxのfindコマンドで正規表現を使用して巨大なディレクトリツリーに再帰的に飛び込み、すべての.c、.cpp、および.hファイルを表示しますが、特定のサブストリングを含む一致を省略します。xargs最終的には、一致するすべてのファイルに対して特定の処理を実行するコマンドに出力を送信したいと思います。grepを介して出力をパイプ処理してfind、これらのサブ文字列を含む一致を削除することはできますが、そのソリューションは、スペースを含むファイル名ではうまく機能しません。そこでfind、改行(空白)の代わりにヌル文字で各ファイル名を終了する's -print0オプションを使用してみました。またxargs -0、スペース区切りの入力の代わりにヌル文字の入力を期待するために使用しましたが、その方法がわかりませんでした。ヌル区切りを渡すfindパイプされたgrepフィルターを正常に通過します。grep -Zは、その点では役に立たなかったようです。

だから私は、より良い正規表現を書いてfind、中間grepフィルターを廃止するだけだと思いました...おそらくsed代替案でしょうか?

いずれにせよ、次のディレクトリの小さなサンプリングでは...

./barney/generated/bam bam.h
./barney/src/bam bam.cpp
./barney/deploy/bam bam.h
./barney/inc/bam bam.h
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h

...出力にすべての.h、.c、および.cppファイルを含めたいのですが、「生成された」および「デプロイ」ディレクトリに表示されるファイルは含めないでください。

ところで、この行全体をbashシェルにカットアンドペーストすることで、この質問のソリューションをテストするためのテストディレクトリ全体(fredbarneyという名前)を作成できます。

mkdir fredbarney; cd fredbarney; mkdir fred; cd fred; mkdir inc; mkdir docs; mkdir generated; mkdir deploy; mkdir src; echo x > inc/dino.h; echo x > docs/info.docx; echo x > generated/dino.h; echo x > deploy/dino.h; echo x > src/dino.cpp; cd ..; mkdir barney; cd barney; mkdir inc; mkdir docs; mkdir generated; mkdir deploy; mkdir src; echo x > 'inc/bam bam.h'; echo x > 'docs/info info.docx'; echo x > 'generated/bam bam.h'; echo x > 'deploy/bam bam.h'; echo x > 'src/bam bam.cpp'; cd ..;

このコマンドは、すべての.h、.c、および.cppファイルを検索します。

find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$"

...しかし、出力をxargsにパイプすると、'bam bam'ファイルはそれぞれ2つの別々の(存在しない)ファイル名として扱われます(ここではls、実際にやりたいことの代用として使用していることに注意してください)出力):

$ find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$" | xargs -n 1 ls
ls: ./barney/generated/bam: No such file or directory
ls: bam.h: No such file or directory
ls: ./barney/src/bam: No such file or directory
ls: bam.cpp: No such file or directory
ls: ./barney/deploy/bam: No such file or directory
ls: bam.h: No such file or directory
ls: ./barney/inc/bam: No such file or directory
ls: bam.h: No such file or directory
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h

findしたがって、-print0および-0引数を使用してこれを拡張できますxargs

$ find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$" -print0 | xargs -0 -n 1 ls
./barney/generated/bam bam.h
./barney/src/bam bam.cpp
./barney/deploy/bam bam.h
./barney/inc/bam bam.h
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h

...これは素晴らしいことですが、出力に「生成された」ディレクトリと「デプロイ」ディレクトリが必要ない点が異なります。だから私はこれを試してみます:

$ find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$" -print0 | grep -v generated | grep -v deploy | xargs -0 -n 1 ls
barney  fred

...明らかに機能しません。そこで、grepで-Zオプションを使用してみましたが(-Zオプションが実際に何をするのか正確にはわかりません)、それも機能しませんでした。だから私はもっと良い正規表現を書くと思いました、findそしてこれは私が思いつくことができる最高のものです:

find . -regextype posix-egrep -regex "(?!.*(generated|deploy).*$)(.+\.(c|cpp|h)$)" -print0 | xargs -0 -n 1 ls

...しかし、bashはそれを気に入らなかった(!。*:イベントが見つかりません、それが何を意味するのか)、そしてそれが問題ではなかったとしても、私の正規表現は私が通常使用している正規表現テスターのWebページでは機能しないようです使用する。

これを機能させる方法はありますか?これは私が望む出力です:

$ find . [----options here----] | [----maybe grep or sed----] | xargs -0 -n 1 ls
./barney/src/bam bam.cpp
./barney/inc/bam bam.h
./fred/src/dino.cpp
./fred/inc/dino.h

...そして、スクリプトと一時ファイルは避けたいと思います。これが私の唯一の選択肢かもしれません。

前もって感謝します!-マーク

4

2 に答える 2

8

これは私のために働く:

find . -regextype posix-egrep -regex '.+\.(c|cpp|h)$' -not -path '*/generated/*' \
       -not -path '*/deploy/*' -print0 | xargs -0 ls -L1d

あなたのバージョンからの変更は最小限です。特定のパスパターンの除外を個別に追加しました。これは簡単だからです。単一引用符で囲んで、シェル補間からそれらを隠しています。

イベントが見つからないのは!、 による履歴拡張の要求として解釈されているためですbash。修正は、二重引用符の代わりに単一引用符を使用することです。

ポップクイズ: の一重引用符で囲まれた文字列内で特別な文字はsh?

回答:のみ 'が特別です (文字列を終了します)。それが究極の安全です。

grepwith -Z( としても知られる--null) は、grep 出力を改行文字ではなくヌル文字で終了させます。あなたが望んでいたのは-z( としても知られる--null-data)で、入力grepのヌル文字を改行文字ではなく行末として解釈させます。これにより、改行の代わりに各ファイル名の後にヌル文字が追加される の出力で期待どおりに動作します。find ... -print0

このようにしていた場合:

find . -regextype posix-egrep -regex '.+\.(c|cpp|h)$' -print0 | \
    grep -vzZ generated | grep -vzZ deploy | xargs -0 ls -1Ld

その後、の入力出力はgrepnullで区切られ、正しく機能していたはずです...ソースファイルの1つに名前が付けられdeployment.cpp始め、スクリプトによって「不思議なことに」除外されるまで。

ちなみに、テストケース ファイル セットを生成するより良い方法を次に示します。

while read -r file ; do
    mkdir -p "${file%/*}"
    touch "$file"
done <<'DATA'
./barney/generated/bam bam.h
./barney/src/bam bam.cpp
./barney/deploy/bam bam.h
./barney/inc/bam bam.h
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h
DATA

とにかく検証のためにこれを行ったので、それを共有して繰り返しから救うと思いました. 二度と何もしないでください!そのためのコンピュータです。

于 2012-07-12T16:34:48.567 に答える