Suchen: Verwenden Sie reguläre Ausdrücke, um alle Dateien mit einem bestimmten Verzeichnisnamen im Pfad abzurufen, jedoch ohne einen anderen bestimmten Verzeichnisnamen im Pfad.

Suchen: Verwenden Sie reguläre Ausdrücke, um alle Dateien mit einem bestimmten Verzeichnisnamen im Pfad abzurufen, jedoch ohne einen anderen bestimmten Verzeichnisnamen im Pfad.

Ich versuche, mit find alle Dateinamen zurückzugeben, die ein bestimmtes Verzeichnis in ihrem Pfad haben, aber nirgendwo im Dateipfad ein anderes bestimmtes Verzeichnis haben. So etwas wie:

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

Ich weiß, dass ich dies möglicherweise auch tun kann, indem ich einen Find-Befehl in einen anderen weiterleite, aber ich würde gerne wissen, wie das mit einem einzelnen regulären Ausdruck geht.

Ich möchte beispielsweise jede Datei, die das Verzeichnis „good“ in ihrem Pfad hat, aber das Verzeichnis „bad“ nirgendwo in ihrem Pfad hat, unabhängig von der Kombination. Einige Beispiele:

/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

Bedenken Sie, dass einige Dateinamen „gut“ oder „schlecht“ enthalten könnten, aber ich möchte nur Verzeichnisnamen berücksichtigen.

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

Meine Recherchen legen nahe, dass ich einen negativen Lookahead und einen negativen Lookbehind verwenden sollte. Bisher hat jedoch nichts von dem, was ich versucht habe, funktioniert. Für etwas Hilfe wäre ich dankbar. Danke.

Antwort1

Wie Inian sagte, ist das nicht nötig -regex(was nicht dem Standard entspricht und die Syntax zwischen den Implementierungen, die ¹ unterstützen, stark variiert -regex).

Sie können -pathhierfür verwenden, Sie können aber auch angeben , finddass Verzeichnisse mit dem Namen nicht betreten werden sollen. badDies wäre effizienter, als alle darin enthaltenen Dateien zu ermitteln und sie später mit herauszufiltern -path:

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

( LC_ALL=Csodass finddas *Platzhalterzeichen nicht bei Dateinamen mit Bytefolgen, die im Gebietsschema keine gültigen Zeichen bilden, blockiert wird).

Oder für mehr als einen Ordnernamen:

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

Mit zshkönnen Sie außerdem Folgendes tun:

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

Oder für die Listen in Arrays:

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

ZunichtWechseln Sie in Verzeichnisse mit dem Namen bad, oder (weniger effizient wie bei -path '*/good/*' ! -path '*/bad/*'):

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

In zsh -o extendedglobist ~dieaußer(und-nicht) Glob-Operator, während ^der Negationsoperator ist und #0 oder mehr vom Vorhergehenden ist, wie bei regulärem Ausdruck *. ${(~j[|])array}Verbindet die Elemente des Arrays mit |, wobei dies |als Glob-Operator und nicht als Literal |mit behandelt wird ~.

In zshkönnten Sie PCRE-Matching nach folgendem verwenden set -o rematchpcre:

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

Aber die Auswertung des Shell-Codes für jede Datei (einschließlich der Dateien in badVerzeichnissen) macht es wahrscheinlich viel langsamer als andere Lösungen.

Beachten Sie auch, dass PCRE (im Gegensatz zu zsh-Globs) an Bytefolgen scheitert, die keine gültigen Zeichen im Gebietsschema bilden, und keine anderen Multibyte-Zeichensätze als UTF-8 unterstützt. Wenn Sie das Gebietsschema wie Coben beschrieben korrigieren find, können Sie beide Probleme für dieses spezielle Muster beheben.

Wenn Sie lieber [[ =~ ]]nur erweiterte Regexp-Übereinstimmungen wie in durchführen möchten bash, können Sie stattdessen auch einfach das PCRE-Modul ( zmodload zsh/pcre) laden und [[ -pcre-match ]]anstelle von verwenden [[ =~ ]], um die PCRE-Übereinstimmung durchzuführen.

Oder Sie können die Filterung folgendermaßen durchführen (GNU oder kompatibel grep -zPvorausgesetzt ):grep

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

( finderkennt aber immer noch alle Dateien in allen badVerzeichnissen).

Ersetzen Sie es tr '\0' '\n'durch xargs -r0 cmd, wenn Sie mit diesen Dateien etwas tun müssen (außer sie zeilenweise auszudrucken).


¹ Auf jeden Fall kenne ich keine findImplementierung, die Perl- oder Vim-ähnliche reguläre Ausdrücke unterstützt, die Sie für Lookaround-Operatoren benötigen würden.

Antwort2

Sie benötigen hierfür keinen regulären Ausdruck. Sie können das -pathPrädikat verwenden, um Verzeichnisse mit einem bestimmten Namen auf jeder Ebene auszuschließen

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

Antwort3

Obwohl es wahrscheinlich weniger effizient (obwohl ich mir nicht sicher bin!) und weniger „korrekt“ ist als finddie leistungsstarke Filterung von (beispielsweise grepfunktioniert naiv hier nicht für Namen, die Zeilenumbruchzeichen enthalten, obwohl diese äußerst selten sind und normalerweise einen Fehler darstellen), ist es oft viel einfacher, einige Instanzen davon zu stapeln, grepdie die Ergebnisse sukzessive mithilfe einfacherer Übereinstimmungen und inverser Übereinstimmungen filtern.-v

Dadurch ist im Umgang mit Teilzeichenfolgen mehr Vorsicht geboten, um sicherzustellen, dass Sie tatsächlich einen Verzeichnisnamen finden. Im Allgemeinen ist die Syntax jedoch viel einfacher zu verstehen und kann möglicherweise alles tun, was Sie brauchen.

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

verwandte Informationen