Oder eine Einführung in die robuste Handhabung von Dateinamen und andere Zeichenfolgen, die in Shell-Skripten übergeben werden.
Ich habe ein Shell-Skript geschrieben, das die meiste Zeit gut funktioniert. Aber es drosselt bei einigen Eingaben (z. B. bei einigen Dateinamen).
Ich habe ein Problem wie das folgende festgestellt:
- Ich habe einen Dateinamen, der ein Leerzeichen enthält
hello world
, und es wurde als zwei separate Dateien behandelthello
undworld
. - Ich habe eine Eingabezeile mit zwei aufeinanderfolgenden Leerzeichen und sie sind in der Eingabe auf eins geschrumpft.
- Führende und nachfolgende Leerzeichen verschwinden in den Eingabezeilen.
- Wenn die Eingabe eines der Zeichen enthält
\[*?
, werden diese manchmal durch Text ersetzt, der eigentlich der Name der Dateien ist. - Es gibt ein Apostroph
'
(oder ein doppeltes Anführungszeichen"
) in der Eingabe und die Dinge wurden nach diesem Punkt seltsam. - Die Eingabe enthält einen Backslash (oder: Ich verwende Cygwin und einige meiner Dateinamen haben Windows-ähnliche
\
Trennzeichen).
Was ist los und wie behebe ich das?
bash
shell
shell-script
quoting
whitespace
Gilles
quelle
quelle
shellcheck
Ihnen helfen, die Qualität Ihrer Programme zu verbessern.Antworten:
Verwenden Sie immer in doppelte Anführungszeichen Variablenersetzungen und Befehlsersetzungen:
"$foo"
,"$(foo)"
Wenn Sie
$foo
nicht in Anführungszeichen setzen, wird Ihr Skript bei Eingaben oder Parametern (oder Befehlsausgaben$(foo)
) mit Leerzeichen oder blockiert\[*?
.Dort können Sie aufhören zu lesen. Na gut, hier noch ein paar mehr:
read
- Um die Eingabe zeilenweise mit demread
eingebauten Code zu lesen , verwenden Siewhile IFS= read -r line; do …
Plain , um
read
Backslashes und Whitespace speziell zu behandeln.xargs
- Vermeidenxargs
. Wenn Sie verwenden müssenxargs
, machen Sie dasxargs -0
. Stattfind … | xargs
, bevorzugtfind … -exec …
.xargs
behandelt Whitespace und die Zeichen\"'
speziell.Diese Antwort gilt für Bourne / POSIX-Stil Schalen (
sh
,ash
,dash
,bash
,ksh
,mksh
,yash
...). Zsh-Benutzer sollten es überspringen und das Ende von lesen. Wann ist eine doppelte Anführungszeichen erforderlich? stattdessen. Wenn Sie alles genau wissen wollen, lesen Sie den Standard oder das Handbuch Ihrer Shell.Beachten Sie, dass die folgenden Erläuterungen einige Näherungswerte enthalten (Aussagen, die in den meisten Fällen zutreffen, jedoch vom umgebenden Kontext oder von der Konfiguration beeinflusst werden können).
Warum muss ich schreiben
"$foo"
? Was passiert ohne die Anführungszeichen?$foo
bedeutet nicht "den Wert der Variablen nehmenfoo
". Es bedeutet etwas viel komplexeres:foo * bar
das Ergebnis dieses Schritt dann die 3-Element - Listefoo
,*
,bar
.foo
, gefolgt von der Liste der Dateien im aktuellen Verzeichnis und schließlichbar
. Wenn das aktuelle Verzeichnis leer ist, ist das Ergebnisfoo
,*
,bar
.Beachten Sie, dass das Ergebnis eine Liste von Zeichenfolgen ist. In der Shell-Syntax gibt es zwei Kontexte: Listenkontext und String-Kontext. Feldaufteilung und Dateinamengenerierung erfolgen nur im Listenkontext, dies ist jedoch die meiste Zeit der Fall. Doppelte Anführungszeichen begrenzen einen Zeichenfolgenkontext: Die gesamte Zeichenfolge mit doppelten Anführungszeichen ist eine einzelne Zeichenfolge, die nicht geteilt werden darf. (Ausnahme:
"$@"
zum Erweitern der Liste der Positionsparameter, entspricht z. B. dem"$@"
Vorhandensein von"$1" "$2" "$3"
drei Positionsparametern. Siehe Was ist der Unterschied zwischen $ * und $ @? )Gleiches gilt für die Befehlsersetzung mit
$(foo)
oder mit`foo`
. Verwenden Sie auf keinen Fall`foo`
: Die Angebotsregeln sind seltsam und nicht portierbar, und alle modernen Shells unterstützen$(foo)
nur intuitive Angebotsregeln, die absolut gleichwertig sind.Die Ausgabe der arithmetischen Substitution wird ebenfalls erweitert, dies ist jedoch normalerweise kein Problem, da sie nur nicht erweiterbare Zeichen enthält (vorausgesetzt, sie
IFS
enthält keine Ziffern oder-
).Siehe Wann sind doppelte Anführungszeichen erforderlich? Weitere Informationen zu den Fällen, in denen Sie die Anführungszeichen weglassen können.
Denken Sie daran, Variablen- und Befehlssubstitutionen immer in Anführungszeichen zu setzen, es sei denn, Sie wollen damit einverstanden sein. Seien Sie vorsichtig: Das Auslassen von Anführungszeichen kann nicht nur zu Fehlern, sondern auch zu Sicherheitslücken führen .
Wie verarbeite ich eine Liste von Dateinamen?
Wenn Sie
myfiles="file1 file2"
mit Leerzeichen schreiben , um die Dateien zu trennen, funktioniert dies nicht mit Dateinamen, die Leerzeichen enthalten. Unix-Dateinamen können andere Zeichen als/
(das ist immer ein Verzeichnisseparator) und Null-Bytes enthalten (die Sie in Shellskripten mit den meisten Shells nicht verwenden können).Gleiches Problem mit
myfiles=*.txt; … process $myfiles
. Wenn Sie dies tun,myfiles
enthält die Variable die 5-stellige Zeichenfolge*.txt
, und wenn Sie schreiben$myfiles
, wird der Platzhalter erweitert. Dieses Beispiel wird tatsächlich funktionieren, bis Sie Ihr Skript ändernmyfiles="$someprefix*.txt"; … process $myfiles
. Wenn auf eingestelltsomeprefix
istfinal report
, funktioniert dies nicht.Um eine Liste beliebiger Art (z. B. Dateinamen) zu verarbeiten, fügen Sie sie in ein Array ein. Dies erfordert mksh, ksh93, yash oder bash (oder zsh, das nicht alle diese Anführungszeichen enthält); Eine einfache POSIX-Shell (z. B. ash oder dash) hat keine Array-Variablen.
Ksh88 verfügt über Array-Variablen mit einer anderen Zuweisungssyntax
set -A myfiles "someprefix"*.txt
(siehe Zuweisungsvariable unter verschiedenen ksh-Umgebungen, wenn Sie die Portierbarkeit von ksh88 / bash benötigen). Bourne / POSIX-Shells haben ein einziges Array, das Array der Positionsparameter, mit"$@"
denen Sie festlegen,set
und das für eine Funktion lokal ist:Was ist mit Dateinamen, die mit beginnen
-
?Beachten Sie in diesem Zusammenhang, dass Dateinamen mit einem
-
(Bindestrich / Minus) beginnen können, was von den meisten Befehlen als Kennzeichnung einer Option interpretiert wird. Wenn Sie einen Dateinamen haben, der mit einem variablen Teil beginnt, stellen Sie sicher, dass Sie diesen voranstellen--
, wie im obigen Snippet. Dies zeigt dem Befehl an, dass das Ende der Optionen erreicht ist. Danach ist alles ein Dateiname, auch wenn es mit beginnt-
.Alternativ können Sie sicherstellen, dass Ihre Dateinamen mit einem anderen Zeichen als beginnen
-
. Absolute Dateinamen beginnen mit/
und können./
am Anfang von relativen Namen hinzugefügt werden . Das folgende Snippet verwandelt den Inhalt der Variablenf
in eine "sichere" Art und Weise, auf dieselbe Datei zu verweisen, von der garantiert wird, dass sie nicht anfängt-
.Beachten Sie abschließend, dass einige Befehle auch nachträglich
-
als Standardeingabe oder Standardausgabe interpretiert werden--
. Wenn Sie auf eine tatsächliche Datei mit dem Namen verweisen-
müssen oder ein solches Programm aufrufen und nicht von stdin lesen oder in stdout schreiben möchten, müssen Sie die oben beschriebenen Schritte ausführen-
. Siehe Was ist der Unterschied zwischen „du -sh *“ und „du -sh ./*“? zur weiteren Diskussion.Wie speichere ich einen Befehl in einer Variablen?
"Befehl" kann drei Dinge bedeuten: einen Befehlsnamen (der Name als ausführbare Datei mit oder ohne vollständigen Pfad oder der Name einer Funktion, eines eingebauten oder eines Alias), einen Befehlsnamen mit Argumenten oder einen Teil des Shell-Codes. Dementsprechend gibt es verschiedene Möglichkeiten, sie in einer Variablen zu speichern.
Wenn Sie einen Befehlsnamen haben, speichern Sie ihn einfach und verwenden Sie die Variable wie gewohnt in doppelten Anführungszeichen.
Wenn Sie einen Befehl mit Argumenten haben, ist das Problem dasselbe wie bei einer Liste der oben genannten Dateinamen: Dies ist eine Liste von Zeichenfolgen, keine Zeichenfolge. Sie können die Argumente nicht einfach in eine einzelne Zeichenfolge mit Leerzeichen dazwischen einfügen, da Sie sonst den Unterschied zwischen Leerzeichen, die Teil von Argumenten sind, und Leerzeichen, die Argumente trennen, nicht erkennen können. Wenn Ihre Shell über Arrays verfügt, können Sie diese verwenden.
Was ist, wenn Sie eine Shell ohne Arrays verwenden? Sie können die Positionsparameter weiterhin verwenden, wenn Sie nichts dagegen haben, sie zu ändern.
Was ist, wenn Sie einen komplexen Shell-Befehl speichern müssen, z. B. mit Umleitungen, Pipes usw.? Oder wenn Sie die Positionsparameter nicht ändern möchten? Anschließend können Sie eine Zeichenfolge mit dem Befehl
eval
erstellen und die integrierte Zeichenfolge verwenden.Beachten Sie die geschachtelten Anführungszeichen in der Definition von
code
: Die einfachen Anführungszeichen'…'
begrenzen ein Zeichenfolgenliteral, sodass der Wert der Variablencode
die Zeichenfolge ist/path/to/executable --option --message="hello world" -- /path/to/file1
. Daseval
eingebaute Kommando weist die Shell an, die als Argument übergebene Zeichenfolge so zu analysieren, als ob sie im Skript enthalten wäre. An diesem Punkt werden also die Anführungszeichen und die Pipe analysiert usw.Verwenden
eval
ist schwierig. Überlegen Sie genau, was wann analysiert wird. Insbesondere können Sie nicht einfach einen Dateinamen in den Code einfügen: Sie müssen ihn in Anführungszeichen setzen, genau wie in einer Quellcodedatei. Es gibt keinen direkten Weg, das zu tun. So etwascode="$code $filename"
bricht , wenn der Dateiname enthält eine beliebige Shell - Sonderzeichen (Leerzeichen,$
,;
,|
,<
,>
, etc.).code="$code \"$filename\""
bricht immer noch auf"$\`
. Geradecode="$code '$filename'"
bricht, wenn der Dateiname a enthält'
. Es gibt zwei Lösungen.Fügen Sie dem Dateinamen eine Anführungszeichen-Ebene hinzu. Der einfachste Weg, dies zu tun, ist das Hinzufügen von einfachen Anführungszeichen und das Ersetzen einzelner Anführungszeichen durch
'\''
.Behalten Sie die Variablenerweiterung im Code bei, damit sie nachgeschlagen wird, wenn der Code ausgewertet wird, und nicht, wenn das Codefragment erstellt wird. Dies ist einfacher, funktioniert aber nur, wenn die Variable zum Zeitpunkt der Codeausführung immer noch denselben Wert aufweist, nicht z. B., wenn der Code in einer Schleife erstellt wird.
Benötigen Sie wirklich eine Variable mit Code? Die natürlichste Art, einem Codeblock einen Namen zu geben, besteht darin, eine Funktion zu definieren.
Was ist
read
los mit ?Ohne
-r
,read
erlaubt Fortsetzungszeilen - dies ist eine einzige logische Eingabezeile:read
Teilt die Eingabezeile in Felder auf, die durch Zeichen in$IFS
(ohne-r
Backslash) getrennt sind. Wenn zum Beispiel der Eingabe eine Zeile mit drei Worten ist, dannread first second third
setztfirst
auf das erste Wort der Eingabe, dersecond
auf das zweite Wort undthird
mit dem dritten Wort. Wenn es mehr Wörter gibt, enthält die letzte Variable alles, was nach dem Setzen der vorhergehenden übrig bleibt. Führende und nachfolgende Leerzeichen werden abgeschnitten.Das Setzen
IFS
auf die leere Zeichenkette vermeidet jegliches Beschneiden. Siehe Warum wird `while IFS = read` so oft verwendet, anstatt` IFS =; während gelesen..`? für eine längere Erklärung.Was ist los mit
xargs
?Das Eingabeformat
xargs
besteht aus durch Leerzeichen getrennten Zeichenfolgen, die wahlweise in einfache oder doppelte Anführungszeichen gesetzt werden können. Kein Standardwerkzeug gibt dieses Format aus.Die Eingabe in
xargs -L1
oderxargs -l
ist fast eine Liste von Zeilen, aber nicht ganz - wenn am Ende einer Zeile ein Leerzeichen steht, ist die folgende Zeile eine Fortsetzungszeile.Sie können verwenden,
xargs -0
wo zutreffend (und wo verfügbar: GNU (Linux, Cygwin), BusyBox, BSD, OSX, aber es ist nicht in POSIX). Das ist sicher, da Null-Bytes in den meisten Daten, insbesondere in Dateinamen, nicht vorkommen können. Verwendenfind … -print0
Sie zum Erstellen einer durch Nullen getrennten Liste von Dateinamen (oderfind … -exec …
wie unten erläutert).Wie verarbeite ich Dateien, die von gefunden wurden
find
?some_command
muss ein externer Befehl sein, es kann keine Shell-Funktion oder ein Alias sein. Wenn Sie eine Shell aufrufen müssen, um die Dateien zu verarbeiten, rufen Sie siesh
explizit auf.Ich habe noch eine andere Frage
Durchsuchen Sie das Anführungszeichen auf dieser Website oder die Shell oder das Shell-Skript . (Klicken Sie auf "Weitere Informationen ...", um einige allgemeine Tipps und eine handverlesene Liste häufig gestellter Fragen anzuzeigen.) Wenn Sie gesucht haben und keine Antwort finden, fragen Sie nach .
quelle
$(( ... ))
(auch$[...]
in einigen Shells), außer inzsh
(sogar in der Sh-Emulation) undmksh
.xargs -0
kein POSIX ist. Mit Ausnahme von FreeBSDxargs
möchten Sie im Allgemeinenxargs -r0
stattxargs -0
.ls --quoting-style=shell-always
ist nicht kompatibel mitxargs
. Versuchen Sietouch $'a\nb'; ls --quoting-style=shell-always | xargs
xargs -d "\n"
, dass Sie beispielsweiselocate PATTERN1 |xargs -d "\n" grep PATTERN2
nach Dateinamen suchen können, die mit PATTERN1 und mit PATTERN2 übereinstimmen . Ohne GNU kann man das zB so machenlocate PATTERN1 |perl -pne 's/\n/\0/' |xargs -0 grep PATTERN1
Während Gilles Antwort ausgezeichnet ist, gehe ich auf seinen Hauptpunkt ein
Wenn Sie mit einer Bash-ähnlichen Shell beginnen, die Worttrennung ausführt, ist der sichere Rat natürlich immer die Verwendung von Anführungszeichen. Die Wortteilung wird jedoch nicht immer durchgeführt
§ Wortteilung
Diese Befehle können fehlerfrei ausgeführt werden
Ich ermutige Benutzer nicht, dieses Verhalten zu übernehmen, aber wenn jemand genau versteht, wann es zu Wortspaltungen kommt, sollte er selbst entscheiden können, wann er Anführungszeichen verwendet.
quelle
foo=$bar
ist OK, aberexport foo=$bar
oderenv foo=$var
nicht (zumindest in einigen Shells). Ein Tipp für Anfänger: Geben Sie immer Ihre Variablen an, es sei denn, Sie wissen, was Sie tun, und Sie haben einen guten Grund, dies nicht zu tun .criteria="-type f"
,find . $criteria
funktioniert es,find . "$criteria"
aber nicht.Soweit ich weiß, gibt es nur zwei Fälle, in denen Erweiterungen in doppelte Anführungszeichen gesetzt werden müssen. In diesen Fällen handelt es sich um die beiden speziellen Shell-Parameter
"$@"
und"$*"
-, die angegeben werden, um in doppelte Anführungszeichen eingeschlossen unterschiedlich zu expandieren. In allen anderen Fällen (möglicherweise mit Ausnahme von Shell-spezifischen Array-Implementierungen) ist das Verhalten einer Erweiterung konfigurierbar - dafür gibt es Optionen.Dies soll natürlich nicht heißen, dass doppelte Anführungszeichen vermieden werden sollten - im Gegenteil, es ist wahrscheinlich die bequemste und robusteste Methode zur Begrenzung einer Erweiterung, die die Shell zu bieten hat. Aber ich denke, da Alternativen bereits fachmännisch erläutert wurden, ist dies ein ausgezeichneter Ort, um zu diskutieren, was passiert, wenn die Shell einen Wert erweitert.
Die Shell in ihrem Herzen und in ihrer Seele (für diejenigen, die solche haben) ist ein Befehlsinterpreter - sie ist ein Parser, wie ein großer, interaktiver
sed
. Wenn Ihre Shell-Anweisung an Leerzeichen oder Ähnlichem erstickt , ist es sehr wahrscheinlich, dass Sie den Interpretationsprozess der Shell nicht vollständig verstanden haben - insbesondere, wie und warum sie eine Eingabeanweisung in einen ausführbaren Befehl übersetzt. Die Aufgabe der Shell ist es:Eingabe akzeptieren
interpretieren und spalten sie richtig in Tokens übersetzten Eingangsworte
Eingabewörter sind die Shell - Syntax Elemente wie
$word
oderecho $words 3 4* 5
Wörter werden immer in Leerzeichen aufgeteilt - das ist nur die Syntax -, aber nur die literalen Leerzeichen, die in der Eingabedatei der Shell zur Verfügung gestellt werden
Erweitern Sie diese gegebenenfalls in mehrere Felder
Felder ergeben sich aus Wort Erweiterungen - sie die endgültige ausführbare Befehl bilden
ausgenommen
"$@"
,$IFS
Feldaufspaltung und Pfadnamenserweiterung ein Eingangswort zu einem einzelnen immer auswerten muß Feld .und dann den resultierenden Befehl auszuführen
Die Leute sagen oft, die Hülle sei ein Klebstoff , und wenn dies zutrifft, dann haften Listen von Argumenten - oder Feldern - an dem einen oder anderen Prozess , wenn dies der Fall ist
exec
. Die meisten Shells behandeln dasNUL
Byte nicht gut - wenn überhaupt - und das liegt daran, dass sie sich bereits darauf aufteilen. Die Shell hatexec
viel zu tun und muss dies mit einemNUL
begrenzten Array von Argumenten tun , die sie dem Systemkern zurexec
Zeit übergibt . Wenn Sie den Begrenzer der Shell mit den begrenzten Daten vermischen würden, würde die Shell dies wahrscheinlich vermasseln. Seine internen Datenstrukturen basieren - wie die meisten Programme - auf diesem Begrenzer.zsh
Insbesondere vermasselt dies nicht.Und genau hier
$IFS
kommt ins$IFS
Spiel. Dies ist ein immer vorhandener und ebenfalls einstellbarer Shell-Parameter, der definiert, wie die Shell die Shell-Erweiterungen von Wort zu Feld aufteilen soll - insbesondere, welche Werte durch diese Felder begrenzt werden sollen.$IFS
teilt Shell-Erweiterungen auf andere Trennzeichen alsNUL
- oder mit anderen Worten, die Shell ersetzt Bytes, die aus einer Erweiterung resultieren, die mit dem Wert von$IFS
withNUL
in ihren internen Datenarrays übereinstimmt . Wenn man es aussehen , dass Sie damit beginnen könnten , um zu sehen , dass jede Feld-split Shell Erweiterung eine ist$IFS
separierten Datenarray.Es ist wichtig zu verstehen, dass
$IFS
nur Erweiterungen begrenzt werden , die nicht bereits anderweitig begrenzt sind - was Sie mit"
doppelten Anführungszeichen tun können . Wenn Sie eine Erweiterung zitieren, begrenzen Sie sie am Anfang und mindestens am Ende ihres Werts. In diesen Fällen$IFS
trifft dies nicht zu, da keine zu trennenden Felder vorhanden sind. In der Tat, eine doppelte Anführungszeichen Expansion zeigt identisches Feldaufteilungsverhalten zu einer nicht notierten Expansion , wennIFS=
auf einen leeren Wert eingestellt ist.Sofern nicht anders angegeben,
$IFS
handelt es sich um eine$IFS
begrenzte Shell-Erweiterung. Der Standardwert ist<space><tab><newline>
- alle drei weisen spezielle Eigenschaften auf, wenn sie in enthalten sind$IFS
. Wohingegen jedes andere Wert$IFS
wird auf einen einzelnen bewerten spezifizierten Feld pro Expansions Auftreten ,$IFS
Leerzeichen - jede dieser drei - angegeben wird auf ein einzelnes Feld pro Expansion elide Sequenz und Vorder- / Hinter Sequenzen sind vollständig elided. Dies ist wahrscheinlich am einfachsten anhand eines Beispiels zu verstehen.Aber das ist nur
$IFS
- nur die Worttrennung oder das Leerzeichen, wie gefragt, also was ist mit den Sonderzeichen ?Die Shell erweitert - standardmäßig - auch bestimmte nicht zitierte Token (wie
?*[
hier an anderer Stelle erwähnt) in mehrere Felder, wenn sie in einer Liste vorkommen. Dies wird als Pfadnamenerweiterung oder Globbing bezeichnet . Es ist ein unglaublich nützliches Tool, und da es nach dem Aufteilen von Feldern in der Syntaxreihenfolge der Shell nicht von $ IFS betroffen ist - Felder, die durch eine Pfadnamenerweiterung generiert werden, werden am Kopf / Ende der Dateinamen selbst abgegrenzt, unabhängig davon, ob Ihr Inhalt enthält alle Zeichen, die sich derzeit in befinden$IFS
. Dieses Verhalten ist standardmäßig aktiviert, kann aber sehr einfach anders konfiguriert werden.Das weist die Shell an, nicht zu globieren . Die Erweiterung des Pfadnamens erfolgt erst, wenn diese Einstellung auf irgendeine Weise rückgängig gemacht wird - beispielsweise, wenn die aktuelle Shell durch einen anderen neuen Shell-Prozess ersetzt wird oder ...
... wird an die Shell ausgegeben. Doppelte Anführungszeichen - wie auch beim
$IFS
Aufteilen von Feldern - machen diese globale Einstellung pro Erweiterung überflüssig. Damit:... wenn die Pfadnamenerweiterung derzeit aktiviert ist, werden wahrscheinlich sehr unterschiedliche Ergebnisse pro Argument erzielt - da das erste nur bis zu seinem Literalwert (das einzige Sternchen, das heißt, überhaupt nicht) und das zweite nur bis zu demselben Wert erweitert wird wenn das aktuelle Arbeitsverzeichnis keine Dateinamen enthält, die möglicherweise übereinstimmen (und es stimmt mit fast allen überein) . Wenn Sie jedoch Folgendes tun:
... die Ergebnisse für beide Argumente sind identisch - das erweitert
*
sich dann nicht.quelle
IFS
tatsächlich funktioniert. Was ich nicht verstehe , ist, warum es jemals eine gute Idee wäreIFS
, etwas anderes als die Standardeinstellung zu wählen.$IFS
.cd /usr/bin; set -f; IFS=/; for path_component in $PWD; do echo $path_component; done
druckt\n
dannusr\n
dannbin\n
. Das erste Feldecho
ist leer, da/
es sich um ein Nullfeld handelt. Die path_components können Zeilenumbrüche oder Leerzeichen oder was auch immer enthalten - das wäre egal, da die Komponenten aufgeteilt wurden/
und nicht der Standardwert. Leute machen esawk
sowieso die ganze Zeit. Ihre Muschel macht es auchIch hatte ein großes Videoprojekt mit Leerzeichen in Dateinamen und Leerzeichen in Verzeichnisnamen. Während
find -type f -print0 | xargs -0
Arbeiten für verschiedene Zwecke und in verschiedenen Schalen, finde ich , dass eine benutzerdefinierte IFS (Eingabefeld Separator) gibt Ihnen mehr Flexibilität zu verwenden , wenn Sie bash verwenden. Das folgende Snippet verwendet bash und setzt IFS nur auf eine neue Zeile. vorausgesetzt, Ihre Dateinamen enthalten keine Zeilenumbrüche:Beachten Sie die Verwendung von Parens, um die Neudefinition von IFS zu isolieren. Ich habe andere Beiträge darüber gelesen, wie man IFS wiederherstellt, aber das ist nur einfacher.
Wenn Sie IFS auf newline setzen, können Sie außerdem Shell-Variablen im Voraus festlegen und diese problemlos ausdrucken. Zum Beispiel kann ich eine Variable V inkrementell vergrößern, indem ich Zeilenumbrüche als Trennzeichen verwende:
und entsprechend:
Jetzt kann ich die Einstellung von V mit
echo "$V"
doppelten Anführungszeichen "auflisten", um die Zeilenumbrüche auszugeben. (Wir danken diesem Thread für die$'\n'
Erklärung.)quelle
zsh
, können Sie verwendenIFS=$'\0'
und verwenden-print0
(zsh
bei Erweiterungen wird kein Globbing ausgeführt, sodass Glob-Zeichen dort kein Problem darstellen).set -f
. Andererseits schlägt Ihr Ansatz bei Dateinamen, die Zeilenumbrüche enthalten, grundsätzlich fehl. Wenn es sich um andere Daten als Dateinamen handelt, schlägt dies auch bei leeren Elementen fehl.Wenn Sie alle oben genannten Sicherheitsaspekte berücksichtigen und davon ausgehen, dass Sie den Variablen vertrauen und diese steuern, können Sie mehrere Pfade mit Leerzeichen verwenden
eval
. Aber sei vorsichtig!quelle