Beachten Sie, dass Dateien auch Zeilenumbrüche in ihrem Dateinamen enthalten können. Deshalb gibt es find -print0und xargs -0.
Daniel Beck
Antworten:
12
Im Idealfall machen Sie das überhaupt nicht so, da es immer schwierig ist, Dateinamen in einem Shell-Skript richtig zu analysieren (korrigieren Sie es für Leerzeichen, Sie haben immer noch Probleme mit anderen eingebetteten Zeichen, insbesondere Zeilenumbrüchen). Dies ist sogar als erster Eintrag auf der BashPitfalls-Seite aufgeführt.
Es gibt jedoch eine Möglichkeit, fast das zu tun, was Sie wollen:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
Denken Sie daran, auch $ibei der Verwendung zu zitieren , um zu vermeiden, dass andere Dinge die Leerzeichen später interpretieren. Denken Sie auch daran, $IFSnach der Verwendung zurückzusetzen, da dies später zu verwirrenden Fehlern führen kann.
Dies hat noch eine weitere Einschränkung: Was innerhalb der whileSchleife passiert , kann in einer Subshell stattfinden, abhängig von der genauen Shell, die Sie verwenden, sodass die variablen Einstellungen möglicherweise nicht bestehen bleiben. Die forLoop-Version vermeidet dies, aber zu dem Preis, dass $IFSSie selbst dann Probleme bekommen, wenn Sie findzu viele Dateien zurückgeben , selbst wenn Sie die Lösung anwenden , um Probleme mit Leerzeichen zu vermeiden .
Irgendwann wird die richtige Lösung für all dies in einer Sprache wie Perl oder Python anstelle von Shell ausgeführt.
Ich mag die Idee, nur Python zu verwenden, um all dies zu vermeiden.
Scott C Wilson
12
Verwenden find -print0und leiten Sie xargs -0es an Ihr kleines C-Programm weiter oder schreiben Sie es an Ihr kleines C-Programm. Dafür wurden -print0und -0wurden erfunden.
Shell-Skripte sind nicht der beste Weg, um Dateinamen mit Leerzeichen zu behandeln: Sie können dies tun, aber es wird klobig.
Sie können das "interne Feldtrennzeichen" ( IFS) auf etwas anderes als Platz für die Aufteilung des Schleifenarguments setzen, z
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
Ich habe IFSnach der Verwendung in find zurückgesetzt, hauptsächlich, weil es gut aussieht, denke ich. Ich habe keine Probleme damit gesehen, es auf Newline zu setzen, aber ich denke, das ist "sauberer".
Eine andere Methode, je nachdem, was Sie mit der Ausgabe von tun möchten find, besteht darin, entweder direkt -execmit dem findBefehl zu verwenden oder ihn zu verwenden -print0und in ihn weiterzuleiten xargs -0. Im ersten Fall findwird dafür gesorgt, dass der Dateiname nicht mehr angezeigt wird. In diesem -print0Fall wird finddie Ausgabe mit einem Nulltrennzeichen gedruckt und anschließend xargsaufgeteilt. Da kein Dateiname dieses Zeichen enthalten kann (was ich weiß), ist dies auch immer sicher. Dies ist meistens in einfachen Fällen nützlich; und ist normalerweise kein guter Ersatz für eine vollständige forSchleife.
Die Verwendung in find -print0Kombination mit xargs -0ist absolut robust gegenüber legalen Dateinamen und eine der erweiterbarsten verfügbaren Methoden. Angenommen, Sie möchten eine Liste aller PDF-Dateien im aktuellen Verzeichnis. Du könntest schreiben
Dadurch wird jedes PDF (via -iname '*.pdf') im aktuellen Verzeichnis ( .) und in jedem Unterverzeichnis gefunden und jedes davon als Argument an den echoBefehl übergeben. Da wir die -n 1Option angegeben haben, xargswird jeweils nur ein Argument an übergeben echo. Hätten wir diese Option weggelassen, xargswären so viele wie möglich an sie vorbeigekommen echo. (Sie können echo short input | xargs --show-limitssehen, wie viele Bytes in einer Befehlszeile zulässig sind.)
Was macht xargsgenau?
Wir können deutlich sehen, welche Auswirkungen dies xargsauf die Eingabe hat - und insbesondere auf die Auswirkungen -n-, indem wir ein Skript verwenden, das seine Argumente genauer wiedergibt als echo.
Ich bin mit den bashBashern nicht einverstanden, da sie bashzusammen mit dem * nix-Tool-Set sehr gut mit Dateien umgehen können (einschließlich solcher, deren Namen Leerzeichen enthalten).
Tatsächlich haben findSie eine genaue Kontrolle über die Auswahl der zu verarbeitenden Dateien ... Auf der Bash-Seite müssen Sie wirklich nur erkennen, dass Sie Zeichenfolgen erstellen müssen bash words. in der Regel durch Verwendung von "doppelten Anführungszeichen" oder eines anderen Mechanismus wie der Verwendung von IFS oder Finds{}
Beachten Sie, dass Sie in den meisten / vielen Situationen IFS nicht einstellen und zurücksetzen müssen. Verwenden Sie IFS einfach lokal, wie in den folgenden Beispielen gezeigt. Alle drei behandeln Leerzeichen in Ordnung. Auch brauchen Sie nicht eine „Standard“ Loop - Struktur, weil FIND \;ist effektiv eine Schleife; Fügen Sie einfach Ihre Schleifenlogik in eine Bash-Funktion ein (wenn Sie kein Standardwerkzeug aufrufen).
Beide Perspektiven haben eine gewisse Gültigkeit. Wenn ich nur an meinen eigenen Dateien gearbeitet habe, habe ich nur find verwendet und mich nicht darum gekümmert, da meine Dateien keine Leerzeichen (oder Zeilenumbrüche!) In ihren Namen haben. Wenn Sie jedoch mit den Dateien anderer Personen arbeiten, müssen Sie robustere Techniken verwenden.
find -print0
undxargs -0
.Antworten:
Im Idealfall machen Sie das überhaupt nicht so, da es immer schwierig ist, Dateinamen in einem Shell-Skript richtig zu analysieren (korrigieren Sie es für Leerzeichen, Sie haben immer noch Probleme mit anderen eingebetteten Zeichen, insbesondere Zeilenumbrüchen). Dies ist sogar als erster Eintrag auf der BashPitfalls-Seite aufgeführt.
Es gibt jedoch eine Möglichkeit, fast das zu tun, was Sie wollen:
Denken Sie daran, auch
$i
bei der Verwendung zu zitieren , um zu vermeiden, dass andere Dinge die Leerzeichen später interpretieren. Denken Sie auch daran,$IFS
nach der Verwendung zurückzusetzen, da dies später zu verwirrenden Fehlern führen kann.Dies hat noch eine weitere Einschränkung: Was innerhalb der
while
Schleife passiert , kann in einer Subshell stattfinden, abhängig von der genauen Shell, die Sie verwenden, sodass die variablen Einstellungen möglicherweise nicht bestehen bleiben. Diefor
Loop-Version vermeidet dies, aber zu dem Preis, dass$IFS
Sie selbst dann Probleme bekommen, wenn Siefind
zu viele Dateien zurückgeben , selbst wenn Sie die Lösung anwenden , um Probleme mit Leerzeichen zu vermeiden .Irgendwann wird die richtige Lösung für all dies in einer Sprache wie Perl oder Python anstelle von Shell ausgeführt.
quelle
Verwenden
find -print0
und leiten Siexargs -0
es an Ihr kleines C-Programm weiter oder schreiben Sie es an Ihr kleines C-Programm. Dafür wurden-print0
und-0
wurden erfunden.Shell-Skripte sind nicht der beste Weg, um Dateinamen mit Leerzeichen zu behandeln: Sie können dies tun, aber es wird klobig.
quelle
Sie können das "interne Feldtrennzeichen" (
IFS
) auf etwas anderes als Platz für die Aufteilung des Schleifenarguments setzen, zIch habe
IFS
nach der Verwendung in find zurückgesetzt, hauptsächlich, weil es gut aussieht, denke ich. Ich habe keine Probleme damit gesehen, es auf Newline zu setzen, aber ich denke, das ist "sauberer".Eine andere Methode, je nachdem, was Sie mit der Ausgabe von tun möchten
find
, besteht darin, entweder direkt-exec
mit demfind
Befehl zu verwenden oder ihn zu verwenden-print0
und in ihn weiterzuleitenxargs -0
. Im ersten Fallfind
wird dafür gesorgt, dass der Dateiname nicht mehr angezeigt wird. In diesem-print0
Fall wirdfind
die Ausgabe mit einem Nulltrennzeichen gedruckt und anschließendxargs
aufgeteilt. Da kein Dateiname dieses Zeichen enthalten kann (was ich weiß), ist dies auch immer sicher. Dies ist meistens in einfachen Fällen nützlich; und ist normalerweise kein guter Ersatz für eine vollständigefor
Schleife.quelle
Verwenden
find -print0
mitxargs -0
Die Verwendung in
find -print0
Kombination mitxargs -0
ist absolut robust gegenüber legalen Dateinamen und eine der erweiterbarsten verfügbaren Methoden. Angenommen, Sie möchten eine Liste aller PDF-Dateien im aktuellen Verzeichnis. Du könntest schreibenDadurch wird jedes PDF (via
-iname '*.pdf'
) im aktuellen Verzeichnis (.
) und in jedem Unterverzeichnis gefunden und jedes davon als Argument an denecho
Befehl übergeben. Da wir die-n 1
Option angegeben haben,xargs
wird jeweils nur ein Argument an übergebenecho
. Hätten wir diese Option weggelassen,xargs
wären so viele wie möglich an sie vorbeigekommenecho
. (Sie könnenecho short input | xargs --show-limits
sehen, wie viele Bytes in einer Befehlszeile zulässig sind.)Was macht
xargs
genau?Wir können deutlich sehen, welche Auswirkungen dies
xargs
auf die Eingabe hat - und insbesondere auf die Auswirkungen-n
-, indem wir ein Skript verwenden, das seine Argumente genauer wiedergibt alsecho
.Beachten Sie, dass Leerzeichen und Zeilenumbrüche perfekt verarbeitet werden.
Dies wäre besonders problematisch bei der folgenden allgemeinen Lösung:
Anmerkungenquelle
Ich bin mit den
bash
Bashern nicht einverstanden, da siebash
zusammen mit dem * nix-Tool-Set sehr gut mit Dateien umgehen können (einschließlich solcher, deren Namen Leerzeichen enthalten).Tatsächlich haben
find
Sie eine genaue Kontrolle über die Auswahl der zu verarbeitenden Dateien ... Auf der Bash-Seite müssen Sie wirklich nur erkennen, dass Sie Zeichenfolgen erstellen müssenbash words
. in der Regel durch Verwendung von "doppelten Anführungszeichen" oder eines anderen Mechanismus wie der Verwendung von IFS oder Finds{}
Beachten Sie, dass Sie in den meisten / vielen Situationen IFS nicht einstellen und zurücksetzen müssen. Verwenden Sie IFS einfach lokal, wie in den folgenden Beispielen gezeigt. Alle drei behandeln Leerzeichen in Ordnung. Auch brauchen Sie nicht eine „Standard“ Loop - Struktur, weil FIND
\;
ist effektiv eine Schleife; Fügen Sie einfach Ihre Schleifenlogik in eine Bash-Funktion ein (wenn Sie kein Standardwerkzeug aufrufen).Und noch zwei Beispiele
'find
also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+instead
\; `)quelle