Ich möchte damit find
Dateien in einer durch Platzhalter eingeschränkten Ordnergruppe suchen, deren Pfadname jedoch Leerzeichen enthält.
Über die Befehlszeile ist dies ganz einfach. Die folgenden Beispiele funktionieren alle.
find te*/my\ files/more -print
find te*/'my files'/more -print
find te*/my' 'files/more -print
Diese finden beispielsweise Dateien in terminal/my files/more
und tepid/my files/more
.
Ich brauche dies jedoch als Teil eines Skripts. Ich brauche etwa Folgendes:
SEARCH='te*/my\ files/more'
find ${SEARCH} -print
Leider kann ich, was auch immer ich tue, in einem find
Befehl innerhalb eines Skripts keine Platzhalter und Leerzeichen mischen. Das obige Beispiel gibt die folgenden Fehler zurück (beachten Sie die unerwartete Verdoppelung des Backslashs):
find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory
Auch der Versuch, Anführungszeichen zu verwenden, schlägt fehl.
SEARCH="te*/'my files'/more"
find ${SEARCH} -print
Dies gibt die folgenden Fehler zurück, da die Bedeutung der Anführungszeichen ignoriert wurde:
find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory
Hier ist noch ein Beispiel.
SEARCH='te*/my files/more'
find ${SEARCH} -print
Wie erwartet:
find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory
Jede Variante, die ich ausprobiert habe, gibt einen Fehler zurück.
Ich habe einen Workaround, der möglicherweise gefährlich ist, weil er zu viele Ordner zurückgibt. Ich konvertiere alle Leerzeichen in ein Fragezeichen (einstelliges Platzhalterzeichen) wie folgt:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?} # Convert every space to a question mark.
find ${SEARCH} -print
Dies entspricht:
find te*/my?files/more -print
Dadurch werden nicht nur die richtigen Ordner zurückgegeben, sondern auch terse/myxfiles/more
, was nicht der Fall sein sollte.
Wie kann ich mein Ziel erreichen? Google hat mir nicht geholfen :(
Antwort1
Der exakt gleiche Befehl sollte in einem Skript einwandfrei funktionieren:
#!/usr/bin/env bash
find te*/my\ files/ -print
Wenn dubrauchenum es als Variable zu haben, wird es etwas komplexer:
#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print
WARNUNG:
Eine solche Verwendung eval
ist nicht sicher und kann zur Ausführung beliebigen und möglicherweise schädlichen Codes führen, wenn Ihre Dateinamen bestimmte Zeichen enthalten können. SieheBash-FAQ 48für Details.
Es ist besser, den Pfad als Argument zu übergeben:
#!/usr/bin/env bash
find "$@" -name "file*"
Ein anderer Ansatz besteht darin, find
dies ganz zu vermeiden und stattdessen die erweiterten Globbing-Funktionen und Globs von Bash zu verwenden:
#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
Mit der globstar
Bash-Option können Sie **
rekursiv abgleichen:
globstar
If set, the pattern ** used in a pathname expansion con‐
text will match all files and zero or more directories
and subdirectories. If the pattern is followed by a /,
only directories and subdirectories match.
Damit es sich 100% wie das Suchen und Einschließen von Dotfiles (versteckten Dateien) verhält, verwenden Sie
#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done
Du kannst echo
sie direkt ausgleichen, ohne die Schleife:
echo te*/my\ files/**
Antwort2
Wie wäre es mit Arrays?
$ tree Desktop/ Documents/
Desktop/
└── my folder
└── more
└── file
Documents/
└── my folder
├── folder
└── more
5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}"
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder
(*)
wird in ein Array aller Elemente erweitert, die dem Platzhalter entsprechen. Und "${SEARCH[@]}"
wird in alle Elemente im Array ( [@]
) erweitert, wobei jedes einzeln in Anführungszeichen gesetzt wird.
Mit Verspätung merke ich, dass Find selbst dazu in der Lage sein sollte. So etwas wie:
find . -path 'D*/my folder/more/'
Antwort3
Es ist mittlerweile etwas veraltet, aber falls es jemandem bei dieser Frage hilft: Die Verwendung des Sortiersymbols von RE [[.space.]]
ohne Anführungszeichen $SEARCH
für die Variable in den Find-Befehlszeilenargumenten funktioniert, solange in den erweiterten Pfadnamen anstelle des Sternchens keine Sonderzeichen vorhanden sind.
set -x
mkdir -p te{flon,nnis,rrine}/my\ files/more
SEARCH=te*/my[[.space.]]files/more
find $SEARCH
geben die folgenden Ergebnisse:
+ mkdir -p 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
+ SEARCH='te*/my[[.space.]]files/more'
+ find 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
teflon/my files/more
tennis/my files/more
terrine/my files/more
Um das Zusammenfassen unerwünschter Zeichen zu verhindern, kann das Asterisk ( *
) durch beliebige Sortierelemente (Zeichen) ersetzt werden:
set -x
mkdir -p -- te{,-,_}{flon,nnis,rrine}/my\ files/more
SEARCH=te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more
find $SEARCH
was zu folgenden Ergebnissen führt:
+ mkdir -p -- 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more' \
'te-flon/my files/more' 'te-nnis/my files/more' 'te-rrine/my files/more' \
'te_flon/my files/more' 'te_nnis/my files/more' 'te_rrine/my files/more'
+ SEARCH='te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more'
+ find 'te-flon/my files/more' 'te_flon/my files/more' 'teflon/my files/more' \
'te-nnis/my files/more' 'te_nnis/my files/more' 'tennis/my files/more' \
'te-rrine/my files/more' 'te_rrine/my files/more' 'terrine/my files/more'
te-flon/my files/more
te_flon/my files/more
teflon/my files/more
te-nnis/my files/more
te_nnis/my files/more
tennis/my files/more
te-rrine/my files/more
te_rrine/my files/more
terrine/my files/more
Beachten Sie, dass zum Verkürzen der Zeile entweder durch oder [.hyphen.]
ersetzt werden kann und entweder durch oder ersetzt werden kann .[.-.]
-
[.underscore.]
[._.]
_
\
Aus Gründen der Lesbarkeit habe ich das Abschneiden von Zeilen mit Backslashes ( ) hinzugefügt.
Antwort4
Ich habe endlich die Antwort herausgefunden.
Fügen Sie allen Leerzeichen einen Backslash hinzu:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }
An dieser Stelle SEARCH
enthält te*/my\ files/more
.
Dann benutze eval
.
eval find ${SEARCH} -print
So einfach ist das! Durch die Verwendung eval
wird die Interpretation ${SEARCH}
einer Variablen umgangen.