Wie übergibt 'find -exec' Dateinamen mit Leerzeichen?

14

Wenn ich ein Verzeichnis habe, das einige Dateien enthält, deren Namen Leerzeichen enthalten, z

$ ls -1 dir1
file 1
file 2
file 3

Ich kann sie alle erfolgreich in ein anderes Verzeichnis kopieren:

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

Die Ausgabe von find dir1 -mindepth 1enthält jedoch nicht maskierte Leerzeichen:

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

Wenn ich print0anstelle von verwende print, enthält die Ausgabe immer noch nicht maskierte Leerzeichen:

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

Um diese Dateien manuell mit zu kopieren cp, müsste ich die Leerzeichen maskieren. aber es scheint, dass dies unnötig ist, wenn cpdie Argumente von kommen find, egal ob ich +oder \;am Ende des Befehls.

Was ist der Grund dafür?

EmmaV
quelle

Antworten:

8

Der findBefehl führt den Befehl direkt aus. Der Befehl, einschließlich des Dateinamen-Arguments, wird von der Shell oder anderen Elementen, die den Dateinamen ändern könnten, nicht verarbeitet. Es ist sehr sicher.

Sie haben Recht, dass Dateinamen, die {}in der findBefehlszeile durch dargestellt werden, nicht umgangen werden müssen .

findÜbergibt den rohen Dateinamen von der Festplatte direkt an die interne Argumentliste des -execBefehls, in Ihrem Fall an den cpBefehl.

RobertL
quelle
1
find..exec
Kurz
2
Die erste Regel des Linux - Club ist , dass Sie dies nicht tun Parse ls
Sergiy Kolodyazhnyy
5

Die Frage besteht aus zwei Teilen:

  • wie geht find es, Programme mit aufzurufen, -execohne auf Probleme mit Leerzeichen in Dateinamen zu stoßen, und
  • was nützt das -print0 Option?

Zum einen findhandelt es sich um einen Systemaufruf, der tatsächlich zu einer Gruppe verwandter Aufrufe gehört, die als "exec" bezeichnet werden . Es übergibt den Dateinamen als Argument direkt an diesen Aufruf, der dann direkt (nach dem Erstellen eines neuen Prozesses) übergeben wird, ohne Informationen über den Dateinamen zu verlieren.

Die POSIX - findFunktion +wird wie folgt erläutert, in der Begründung :

Ein Merkmal des SVR4- findDienstprogramms war der -execTerminator + des Primärs. Auf diese Weise konnten Dateinamen, die Sonderzeichen (insbesondere Zeilenvorschubzeichen ) enthielten, ohne die Probleme gruppiert werden, die bei der Weiterleitung solcher Dateinamen auftreten xargs. Andere Implementierungen haben andere Möglichkeiten hinzugefügt, um dieses Problem zu umgehen, insbesondere eine -print0primäre, die Dateinamen mit einem Null-Byte-Terminator schrieb. Dies wurde hier berücksichtigt, aber nicht übernommen. Die Verwendung eines Nullterminators bedeutete, dass jedes Dienstprogramm, das die -print0Ausgabe von find verarbeiten sollte, eine neue Option hinzufügen musste, um die Nullterminatoren zu analysieren, die es jetzt lesen würde.

Das "vor allem eine -print0primäre" bezieht sich auf GNU findund xargsdie das Problem auf eine andere Weise lösen. Es wird auch von FreeBSD findund unterstützt xargs. Wenn Sie dem Aufruf eine -0Option hinzugefügt haben (siehe Handbuchseite ), xargsakzeptiert dieses Programm Zeilen, die mit "Null-Byte" -Zeichen abgeschlossen sind. Im Gegenzug xargsruft exec -Funktionen zu tun , seine Arbeit. Der Hauptunterschied zwischen dem -print0und -0Merkmal gegenüber dem+ Feature besteht darin, dass das erstere die Dateinamen über eine Pipe übergibt, während das letztere dies nicht tut. Entwickler finden für fast alle Funktionen Verwendung. Die Rohre sind keine Ausnahme.

Zurück zum Beispiel von OP, das eine -tOption verwendet, um cp: das in POSIX cp nicht gefunden wird . Es handelt sich vielmehr um eine Erweiterung (auch bekannt als "Nicht-Standard-Feature"), die von GNU cp bereitgestellt wird . Die -0Erweiterung von xargswürde dieses Beispiel nicht verbessern, aber es gibt andere Fälle, in denen es effektiv genutzt werden kann - bedenken Sie, dass es die portable Alternative gibt +, die GNU findakzeptiert.

Thomas Dickey
quelle
-1

( Dies sollte ein Kommentar sein, aber er ist zu groß. )

Für diejenigen, die Dinge ausprobieren möchten:

Erstellen Sie ein Skript, das die übergebenen Positionsparameter auflistet, und rufen Sie es auf list_positional_parameters.sh.

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

Führen Sie findes in einem Verzeichnis $ dir aus:

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

Wie erwartet gibt es in allen Aufrufen nur einen einzigen Parameter, den Dateinamen, unabhängig davon, ob der Name Leerzeichen enthält oder nicht.

David Tonhofer
quelle
1
Sie können auch printfgerne printf '"%s"\n' "$@"alle angegebenen Positionsargumente zur visuellen Kontrolle ausdrucken.
Kusalananda