Ich versuche, ein Skript mit einer Liste von Dateinamen aufzurufen, die von gesammelt wurden find
. Nichts Besonderes, nur so etwas:
$ myscript `find . -name something.txt`
Das Problem ist, dass einige der Pfadnamen Leerzeichen enthalten, sodass sie bei der Argumenterweiterung in zwei ungültige Namen aufgeteilt werden. Normalerweise würde ich die Namen mit Anführungszeichen umgeben, aber hier werden sie durch die Backquote-Erweiterung eingefügt. Ich habe versucht, die Ausgabe find
jedes Dateinamens mit Anführungszeichen zu filtern , aber bis Bash sie sieht, ist es zu spät, sie zu entfernen, und sie werden als Teil des Dateinamens behandelt:
$ myscript `find . -name something.txt | sed 's/.*/"&"/'`
No such file or directory: '"./somedir/something.txt"'
Ja, das sind die Regeln für die Verarbeitung der Befehlszeile, aber wie komme ich darum herum?
Das ist peinlich, aber ich finde nicht den richtigen Ansatz. Endlich habe ich herausgefunden, wie es geht xargs -0 -n 10000
... aber es ist ein so hässlicher Hack, dass ich immer noch fragen möchte: Wie zitiere ich die Ergebnisse der Backquote-Erweiterung oder erreiche den gleichen Effekt auf eine andere Weise?
Edit: Ich war über die Tatsache verwirrt , dass xargs
tut sammle alle Argumente in einer einzigen Argumentliste, sofern es ihm sonst oder Systemgrenzen könnte überschritten werden. Vielen Dank an alle, die mich gerade gesetzt haben! Andere, denken Sie daran, wenn Sie die akzeptierte Antwort lesen, da nicht direkt darauf hingewiesen wird.
Ich habe die Antwort akzeptiert, aber meine Frage bleibt: Gibt es keine Möglichkeit, Leerzeichen bei der Backtick- (oder $(...)
) Erweiterung zu schützen ? (Beachten Sie, dass die akzeptierte Lösung keine bash-Antwort ist.)
IFS="
, newline,"
). Muss das Skript jedoch über alle Dateinamen ausgeführt werden? Wenn nicht, können Sie das Skript für jede Datei mit find selbst ausführen.Antworten:
Mit einigen Implementierungen von
find
undxargs
wie folgt können Sie Folgendes ausführen .oder einfach
find
:Beispiel
Angenommen, ich habe das folgende Beispielverzeichnis.
Sagen wir jetzt, ich habe das für
./myscript
.Nun, wenn ich den folgenden Befehl ausführen.
Oder wenn ich das 2. Formular so benutze:
Einzelheiten
find + xargs
Die beiden oben genannten Methoden sehen zwar unterschiedlich aus, sind jedoch im Wesentlichen gleich. Das erste ist, die Ausgabe von find zu nehmen und sie mit NULLs (
\0
) über den-print0
Schalter find zu teilen . Derxargs -0
ist speziell dafür ausgelegt, mit NULL geteilte Eingaben zu verarbeiten. Diese Nicht-Standard-Syntax wurde von GNU eingeführtfind
undxargs
ist heutzutage auch in einigen anderen wie den neuesten BSDs zu finden. Die-r
Option wird benötigt, um einen Aufruf zu vermeiden,myscript
wennfind
nichts mit GNU,find
aber nicht mit BSDs gefunden wird.ANMERKUNG: Dieser gesamte Ansatz hängt von der Tatsache ab, dass Sie niemals eine Zeichenfolge passieren, die übermäßig lang ist. Wenn dies der
./myscript
Fall ist, wird ein zweiter Aufruf von mit dem Rest der nachfolgenden Ergebnisse von find gestartet.mit + finden
Das ist die Standardmethode (obwohl sie erst vor relativ kurzer Zeit (2005) zur GNU-Implementierung von hinzugefügt wurde
find
). Die Fähigkeit zu tun, was wir tun,xargs
ist buchstäblich eingebautfind
. Sofind
wird eine Liste der Dateien finden und dann diese Liste als so viele Argumente übergeben , wie auf den Befehl passen kann nach Angabe-exec
(beachten Sie, dass{}
nur zuletzt kurz vor sein kann ,+
in diesem Fall), die Befehle mehrmals ausgeführt wird, wenn nötig.Warum kein Zitat?
Im ersten Beispiel nehmen wir eine Abkürzung, indem wir die Probleme mit dem Zitieren vollständig vermeiden und die Argumente durch NULL trennen. Wenn
xargs
diese Liste angezeigt wird, wird sie angewiesen, die NULL-Werte aufzuteilen, um die einzelnen Befehlsatome effektiv zu schützen.Im zweiten Beispiel behalten wir die internen Ergebnisse bei
find
und wissen so, was jedes Dateiatom ist, und garantieren, dass es angemessen behandelt wird, wodurch die Quotierung der Ergebnisse vermieden wird.Maximale Größe der Kommandozeile?
Diese Frage wird von Zeit zu Zeit gestellt, daher füge ich sie als Bonus zu dieser Antwort hinzu, hauptsächlich, damit ich sie in Zukunft finden kann. Sie können Folgendes verwenden
xargs
, um die Grenzen der Umgebung zu ermitteln:quelle
+
Argument dafürfind
(und du verwendest es auch+
in der Prosa, also habe ich deine Erklärung das erste Mal verpasst). Aber mehr auf den Punkt gebracht, ich hätte falsch verstanden, wasxargs
standardmäßig tut !!! In drei Jahrzehnten, in denen ich Unix verwendet habe, hatte ich bis jetzt noch nie eine Verwendung dafür, aber ich dachte, ich kenne meine Toolbox ...xargs
ist ein Teufel eines Befehls. Man muss es und seinefind
Manpages viele Male durchlesen, um herauszufinden, was sie können. Mai der Schalter sind gegensätzlich, so dass die Verwirrung beiträgt.$(..)
sondern verwenden Sie jetzt. Es behandelt automatisch das Verschachteln von Anführungszeichen usw. Backticks werden nicht mehr empfohlen.Im obigen
find
findet alle die passenden Dateinamen und stellt sich als Argument anmyscript
. Dies funktioniert mit Dateinamen unabhängig von Leerzeichen oder anderen ungeraden Zeichen.Wenn alle Dateinamen in eine Zeile passen, wird myscript einmal ausgeführt. Wenn die Liste zu lang ist, um von der Shell verarbeitet zu werden, führt find myscript nach Bedarf mehrmals aus.
MEHR: Wie viele Dateien passen auf eine Befehlszeile?
man find
sagt, dassfind
es Befehlszeilen erstellt, "ähnlich wie xargs seine erstellt". Undman xargs
dass die Grenzwerte systemabhängig sind und dass Sie sie durch Ausführen bestimmen könnenxargs --show-limits
. (getconf ARG_MAX
ist auch eine möglichkeit). Unter Linux liegt das Limit normalerweise (aber nicht immer) bei 2 Millionen Zeichen pro Befehlszeile.quelle
Ein paar Ergänzungen zu @ slms feiner Antwort.
Die Beschränkung der Größe der Argumente hängt vom
execve(2)
Systemaufruf ab (tatsächlich hängt sie von der kumulativen Größe der Argument- und Umgebungszeichenfolgen und -zeiger ab). Wennmyscript
es in einer Sprache geschrieben ist, die Ihre Shell interpretieren kann, müssen Sie es möglicherweise nicht ausführen . Sie können Ihre Shell einfach interpretieren lassen, ohne einen anderen Interpreter ausführen zu müssen.Wenn Sie das Skript ausführen als:
Es ist wie:
Außer, dass es von einem untergeordneten Element der aktuellen Shell interpretiert wird, anstatt es auszuführen (was schließlich das Ausführen
sh
(oder was auch immer in der She-Bang-Zeile angegeben ist, falls vorhanden) mit noch mehr Argumenten beinhaltet).Jetzt können Sie
find -exec {} +
den.
Befehl natürlich nicht mehr verwenden , da.
er ein integrierter Befehl der Shell ist und von der Shell und nicht von ausgeführt werden mussfind
.Mit
zsh
ist es einfach:Oder:
Allerdings
zsh
würden Sie es nichtfind
in erster Linie brauchen, da die meisten Funktionen inzsh
Globbing integriert sind.bash
Variablen dürfen jedoch keine NUL-Zeichen enthalten, daher müssen Sie einen anderen Weg finden. Ein Weg könnte sein:Sie können auch rekursives Globbing
globstar
im zsh-Stil mit der Option inbash
4.0 und höher verwenden:Beachten Sie, dass
**
Symlinks zu Verzeichnissen folgten, bis es inbash
4.3 behoben wurde . Beachten Sie auch, dass Globbing-Qualifiziererbash
nicht implementiert werden,zsh
sodass Sie dort nicht alle Funktionen nutzenfind
können.Eine andere Alternative wäre die Verwendung von GNU
ls
:Die obigen Verfahren können auch verwendet werden , wenn Sie sicher machen wollen
myscript
wird ausgeführt nur einmal (andernfalls , wenn die Argumentliste zu groß ist). In neueren Linux-Versionen können Sie diese Einschränkung in der Argumentliste wie folgt erhöhen und sogar aufheben:(1 GB Stack-Größe, von der ein Viertel für die arg + env-Liste verwendet werden kann).
(keine Begrenzung)
quelle
In den meisten Systemen ist die Länge einer Befehlszeile, die mit
xargs
oder an ein Programm übergeben wird, begrenzt-exec command {} +
. Vonman find
:Invocations werden viel weniger, aber nicht garantiert sein. Was Sie tun sollten, ist, die durch NUL getrennten Dateinamen im Skript von stdin zu lesen, was auf der Grundlage eines Befehlszeilenarguments möglich ist
-o -
. Ich würde etwas machen wie:und implementieren Sie die Optionsargumente
myscript
entsprechend.quelle
xargs
. Ihre Lösung ist zwar die robusteste, aber in diesem Fall übertrieben.Nein, das gibt es nicht. Warum das?
Bash hat keine Möglichkeit zu wissen, was geschützt werden sollte und was nicht.
Es gibt keine Arrays in der Unix-Datei / Pipe. Es ist nur ein Bytestream. Der Befehl innerhalb von
``
oder$()
gibt einen Stream aus, der schluckt und als einzelne Zeichenfolge behandelt. In diesem Fall haben Sie nur zwei Möglichkeiten: Setzen Sie es in Anführungszeichen, um es als eine Zeichenfolge zu behalten, oder setzen Sie es nackt, damit die Bash es entsprechend dem konfigurierten Verhalten aufteilt.Wenn Sie also ein Array definieren möchten, müssen Sie ein Byte-Format definieren, das ein Array enthält, und das ist, was Werkzeuge mögen
xargs
undfind
tun: Wenn Sie sie mit dem-0
Argument ausführen , arbeiten sie nach einem binären Array-Format, mit dem Elemente abgeschlossen werden das Null-Byte, das dem ansonsten undurchsichtigen Byte-Stream Semantik hinzufügt.bash
Kann leider nicht so konfiguriert werden, dass Zeichenfolgen auf dem Null-Byte aufgeteilt werden. Vielen Dank an /unix//a/110108/17980, dass Sie uns das gezeigt habenzsh
.xargs
Sie möchten, dass Ihr Befehl einmal ausgeführt wird, und Sie sagten, dies
xargs -0 -n 10000
löst Ihr Problem. Wenn dies nicht der Fall ist, wird sichergestellt, dass Ihr Befehl mehr als einmal ausgeführt wird, wenn Sie mehr als 10000 Parameter haben.Wenn Sie möchten, dass es nur einmal ausgeführt wird oder fehlschlägt, müssen Sie das
-x
Argument und ein-n
Argument angeben, das größer als das-s
Argument ist (wirklich: groß genug, dass eine ganze Reihe von Argumenten mit der Länge Null plus dem Namen des Befehls nicht hineinpassen die-s
Größe). ( Mann Xargs , siehe Auszug weit unten)Das System, auf dem ich mich gerade befinde, hat einen Stack, der auf ca. 8 Millionen beschränkt ist. Hier ist mein Limit:
Bash
Wenn Sie keinen externen Befehl einbeziehen möchten, ist die while-read-Schleife, die ein Array speist, wie in /unix//a/110108/17980 gezeigt , die einzige Möglichkeit für die Bash, Dinge zu trennen das Null-Byte.
Die Idee, das Skript als Quelle
( . ... "$@" )
zu verwenden, um die Stapelgrößenbeschränkung zu umgehen, ist cool (ich habe es ausprobiert, es funktioniert!), Aber wahrscheinlich nicht wichtig für normale Situationen.Die Verwendung eines speziellen fd für die Prozessleitung ist wichtig, wenn Sie etwas anderes von stdin lesen möchten, es sonst aber nicht benötigen.
Der einfachste "native" Weg für den täglichen Haushaltsbedarf:
Wenn Sie möchten, dass Ihr Prozessbaum sauber und ansprechend aussieht, können Sie mit dieser Methode
exec mynonscript "${files[@]}"
den Bash-Prozess aus dem Speicher entfernen und ihn durch den aufgerufenen Befehl ersetzen.xargs
bleibt immer im Speicher, während der aufgerufene Befehl ausgeführt wird, auch wenn der Befehl nur einmal ausgeführt wird.Was gegen die native Bash-Methode spricht, ist Folgendes:
bash ist nicht für das Array-Handling optimiert.
Mann Xargs :
quelle
ls "what is this"
vsls `echo '"what is this"'`
. Jemand hat es versäumt, die Angebotsverarbeitung für das Ergebnis von Backquotes zu implementieren.$(...)
) Expansion zu schützen ?". Es erscheint daher angebracht, die Verarbeitung zu ignorieren, die in dieser Situation nicht erfolgt.bash
es nicht von Haus aus unterstützt wird, wie es anscheinend derzsh
Fall ist.printf "%s\0"
undxargs -0
umgangen, in der ein Zwischenwerkzeug Parameter durch einen von einer Shell analysierten String übergeben hat. Das Zitieren kommt immer zurück, um Sie zu beißen.