Wie übergebe ich das Platzhalterzeichen „*“ über eine Variable im Skript an den Pfadparameter des Suchbefehls?

Wie übergebe ich das Platzhalterzeichen „*“ über eine Variable im Skript an den Pfadparameter des Suchbefehls?

Ich möchte damit findDateien 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/moreund 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 findBefehl 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 evalist 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, finddies 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 globstarBash-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 echosie 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 $SEARCHfü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 SEARCHenthält te*/my\ files/more.

Dann benutze eval.

eval find ${SEARCH} -print

So einfach ist das! Durch die Verwendung evalwird die Interpretation ${SEARCH}einer Variablen umgangen.

verwandte Informationen