2015-03-26 4 views
6

Ich habe einen Befehl, die UUID für Dateien zu erzeugen versucht:Können xargs einen Subshell-Befehl für jedes Argument ausführen?

find -printf "%P\n"|sort|xargs -L 1 echo $(uuid) 

Aber im Ergebnis xargs nur Subshell die $(uuid) Ausführung einmal:

8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file1 
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file2 
8aa9e7cc-d3b2-11e4-83a6-1ff1acc22a7e file3 

Gibt es einen Einzeiler (dh keine Funktion), um xargs zu erhalten, um einen Subshell-Befehl für jede Eingabe auszuführen?

+1

@TomFenech: '-n 1 'würde von jedem Leerzeichen tatsächlich geteilt, ob Line-Interieur oder nicht, also würde der Befehl mit Pfaden mit eingebetteten Leerzeichen brechen; "-L 1" kommt der Absicht näher, indem es eine zeilenweise Verarbeitung durchführt, aber Worttrennung wird immer noch auf jede Zeile angewendet, so dass potentiell _ multiple_ Argumente pro Eingang an echo übergeben werden oder möglicherweise keine Probleme verursachen). Der robuste Ansatz ist die Verwendung von "-I", wie in der akzeptierten Antwort. – mklement0

Antwort

10

Dies ist, weil die $(uuid) in der aktuellen Shell erweitert wird. Sie könnten eine Shell explizit aufrufen:

find -printf "%P\n"| sort | xargs -I '{}' bash -c 'echo $(uuid) {}' 

Btw, ich würde den folgenden Befehl verwenden:

find -exec bash -c 'echo "$(uuid) ${1#./}"' -- '{}' \; 

ohne xargs.

+2

Schön gemacht; aber "-n 1" ist nicht nur überflüssig, weil "-I" Zeile-für-Zeile-Verarbeitung impliziert, "-n 1" würde tatsächlich durch _alles_ Leerzeichen getrennt, egal ob linienförmig oder nicht. Während "-L 1" eine zeilenweise Verarbeitung ausführt, wird die Wortaufteilung immer noch auf jede Zeile angewendet, während "-I" die gesamte Zeile als _single_ -Argument behandelt. – mklement0

+1

@ mklement0 Vielen Dank, dass Sie darauf hingewiesen haben! Bearbeitet, dass – hek2mgl

2

mit einer for-Schleife:

for i in $(find -printf "%P\n" | sort) ; do echo "$(uuid) $i"; done 

Edit: Ein anderer Weg, dies zu tun:

find -printf "%P\0" -exec uuid -v 4 \; | sort | awk -F'\0' '{ print $2 " " $1}' 

Dies gibt das durch den UUID gefolgt Dateinamen (kein Subshell erforderlich) Damit die Sortierung passieren kann, werden die beiden durch null getrennten Spalten vertauscht.

+0

Dies funktioniert auch und ist eine etwas einfachere Version zu lesen sowie nicht den Overhead einer neuen Bash auf jedem Argument. Wenn ich den Kredit teilen könnte, würde ich. Vielen Dank. – adelphus

+0

Die Verwendung einer Shell-Schleife in diesem Fall ist aus Performance-Gründen eine gute Idee, aber es ist besser, eine 'while'-Schleife zu verwenden, da 'for' beispielsweise mit Dateinamen mit eingebetteten Leerzeichen bricht - siehe http: //mywiki.wooledge .org/DontReadLinesWithFor – mklement0

+1

@ mklement0 das ist sehr wahr danke; Wie auch immer, ich entschied mich dafür, dass die Schleife besser ist. –

2

hek2mgl's answer erklärt das Problem gut und seine Lösung funktioniert gut; Diese Antwort betrachtet Leistung.

Die akzeptierte Antwort ist ein bisschen langsam, weil es einen bash Prozess für jede Eingabezeile erstellt.

Während xargs ist im allgemeinen vorzuziehen, und schneller als ein muschelCodeSchleife, in diesem speziellen Fall die Rollen umgekehrt sind, weil Shell-Funktionalität in jeder Iteration benötigt wird.

Die folgende alternative Lösung verwendet eine while Schleife die Eingangsleitungen zu verarbeiten, und auf meiner Maschine, ist etwa doppelt so schnell als xargs Lösung.

find . -printf "%P\n" | sort | while IFS= read -r f; do echo "$(uuid) $f"; done 

Wenn Sie Dateinamen mit eingebetteten Zeilenumbrüche betroffen sind (sehr selten) und verwenden GNU Dienstprogramme, könnten Sie NUL Bytes als Trennzeichen verwenden:

find . -printf "%P\0" | sort -z | while IFS= read -d '' -r f; do echo "$(uuid) $f"; done 

aktualisieren: Die schnellste Ansatz ist eine Shell-Schleife überhaupt nicht zu verwenden, wie durch ᴳᵁᴵᴰᴼ's clever answer. Siehe unten für eine portable Version seiner Antwort.


Hinweis zur Kompatibilität:

Die find des OP-Befehl die Verwendung von impliziert GNUfind (Linux) und nutzt Funktionen (-printf), die nicht auf anderen Plattformen arbeiten können.

Hier ist eine portable Version von ᴳᵁᴵᴰᴼ's answer, die nur POSIX-kompatible Funktionen von find (und awk) verwendet.
Beachten Sie jedoch, dass uuid kein POSIX-Dienstprogramm ist; da Linux und BSD-ähnliche Systeme (einschließlich OSX) einen uuidgen Dienstprogramm haben, verwendet der Befehl, dass statt:

find . -exec printf '%s\t' {} \; -exec uuidgen \; | 
    awk -F '\t' '{ sub(/.+\//,"", $1); print $2, $1 }' | sort -k2 
Verwandte Themen