検索:正規表現を使用して、パスに特定のディレクトリ名があるがパスに別の特定のディレクトリ名を持たないすべてのファイルを取得します。

検索:正規表現を使用して、パスに特定のディレクトリ名があるがパスに別の特定のディレクトリ名を持たないすべてのファイルを取得します。

findを使用して、パスに特定のディレクトリがあるがファイルパスに他の特定のディレクトリが含まれていないすべてのファイル名を返そうとします。それは次のとおりです。

myRegex= <regex> 
targetDir= <source directory>
find $targetDir -regex $myRegex -print

ある find コマンドを別のコマンドにパイプすることでこれを行うこともできますが、単一の正規表現を使用してこれを実行する方法を知りたいです。

たとえば、すべてのファイルのパスに「good」ディレクトリがありますが、組み合わせに関係なく、パスのどこにも「bad」ディレクトリがないことを望みます。いくつかの例:

/good/file_I_want.txt #Captured
/good/bad/file_I_dont_want.txt #Not captured

/dir1/good/file_I_want.txt #Captured
/dir2/good/bad/file_I_dont_want.txt #Not captured

/dir1/good/dir2/file_I_want.txt #Captured
/dir1/good/dir2/bad/file_I_want.txt #Not captured

/bad/dir1/good/file_I_dont_want.txt #Not captured

一部のファイル名には「良い」または「悪い」を含めることができますが、ディレクトリ名だけを考慮したいと思います。

/good/bad.txt #Captured
/bad/good.txt #Not captured

私の研究では、否定的な予測と否定的な予測を使用する必要があることがわかりました。しかし、これまで試したことは何も機能しませんでした。助けてくれてありがとう。ありがとうございます。

答え1

Inianが言ったように、そうする必要はありません-regex(非標準であり、構文はサポートされている実装によって大きく異なります-regex)。

これを使用することもできますが、名前付きディレクトリに移動しないように指示すること-pathもできます。これは、後でそのファイルをフィルタリングできるように、その中のすべてのファイルを検索するよりも効率的です。findbad-path

LC_ALL=C find . -name bad -prune -o -path '*/good/*.txt' -type f -print

LC_ALL=Cしたがって、ワイルドカードは、バイトfindシーケンスが*ロケールで有効な文字を形成しないファイル名をブロックしません。)

または複数のフォルダ名の場合:

LC_ALL=C find . '(' -name bad -o -name worse ')' -prune -o \
  '(' -path '*/good/*' -o -path '*/better/*' ')' -name '*.txt' -type f -print

を使用すると、zsh次の操作も実行できます。

set -o extendedglob # best in ~/.zshrc
print -rC1 -- (^bad/)#*.txt~^*/good/*(ND.)
print -rC1 -- (^(bad|worse)/)#*.txt~^*/(good|better)/*(ND.)

または配列リストの場合:

good=(good better best)
bad=(bad worse worst)
print -rC1 -- (^(${(~j[|])bad})/)#*.txt~^*/(${(~j[|])good})/*(ND.)

到着いいえbadまたは(など効率が悪い)という名前のディレクトリを入力します-path '*/good/*' ! -path '*/bad/*'

print -rC1 -- **/*.txt~*/bad/*~^*/good/*(ND.)

zsh -o extendedglob~うんとは別に(NAND)ワイルドカード演算子while^は否定演算子であり、#regexpのようにゼロ個以上の先行コンテンツです*${(~j[|])array}配列の要素を連結し||それをリテラルではなくグローバル演算子として扱うために|使用されます~

では、zsh一致後にPCREを使用できますset -o rematchpcre

set -o rematchpcre
regex='^(?!.*/bad/).*/good/.*\.txt\Z'
print -rC1 -- **/*(ND.e['[[ $REPLY =~ $regex ]]'])

ただし、すべてのファイル(ディレクトリ内のファイルを含む)のシェルコードを評価することは、bad他のソリューションよりはるかに遅くなる可能性があります。

また、PCRE(zsh globとは反対)は、ロケールで有効な文字を形成しないバイトシーケンスをブロックし、UTF-8以外のマルチバイト文字セットをサポートしないことに注意してください。ロケールをC上記のように変更すると、findこの特定のモードの問題が解決されます。

[[ =~ ]]のように拡張正規表現マッチングを実行したい場合は、PCREマッチングを実行する代わりにPCREモジュール()をbashロードして使用することもできます。zmodload zsh/pcre[[ -pcre-match ]][[ =~ ]]

または、次のコマンドを使用してフィルタリングできますgrep -zP(GNUgrepまたは互換性があると仮定)。

regex='^(?!.*/bad/).*/good/.*\.txt\Z'
find . -type f -print0 |
  LC_ALL=C grep -zPe "$regex" |
  tr '\0' '\n'

findすべてのディレクトリbad内のすべてのファイルがまだ見つかりますが)。

これらのファイルに対して操作を実行する必要がある場合(1行に1つずつ印刷することを除く)tr '\0' '\n'に置き換えます。xargs -r0 cmd


findとにかく、私はツアー演算子として必要なPerl様またはVim様の正規表現をサポートする実装を知りません。

答え2

このために正規表現を使用する必要はありません。述語を使用して、任意の-pathレベルで特定の名前のディレクトリを除外できます。

find . -type f -path '*/good/*' '!' -path '*/bad/*'

答え3

おそらく強力なフィルタリングよりも効率が低く(確かではありませんが)、「正確さ」は少なくなりますfind(たとえば、grepここで無実の内容は改行文字を含む名前には適用されません。より簡単です。grepより単純なマッチングとリバースマッチングを使用して結果を継続的にフィルタリングするために、いくつかのインスタンスをスタックします。-v

実際にディレクトリ名を見つけるには、部分文字列にもう少し注意が必要ですが、一般的に理解しやすく、必要なすべてのことを行う構文を提供します。

find ./ | grep "/good/" | grep -v "/bad/" | grep '\.txt$'

関連情報