Wie kann ich xargs veranlassen, dass der Befehl für jede eingegebene Zeile genau einmal ausgeführt wird? Das Standardverhalten besteht darin, die Zeilen zu bündeln und den Befehl einmal auszuführen, wobei jeder Instanz mehrere Zeilen übergeben werden.
Von http://en.wikipedia.org/wiki/Xargs :
find/path -type f -print0 | xargs -0 rm
In diesem Beispiel liefert find die Eingabe von xargs mit einer langen Liste von Dateinamen. xargs teilt diese Liste dann in Unterlisten auf und ruft rm einmal für jede Unterliste auf. Dies ist effizienter als diese funktional äquivalente Version:
find/path -type f -exec rm '{}' \;
Ich weiß, dass find das Flag "exec" hat. Ich zitiere nur ein anschauliches Beispiel aus einer anderen Quelle.
Folgendes funktioniert nur, wenn Ihre Eingabe keine Leerzeichen enthält:
xargs -L 1
xargs --max-lines=1 # synonym for the -L option
von der Manpage:
-L max-lines
Use at most max-lines nonblank input lines per command line.
Trailing blanks cause an input line to be logically continued on
the next input line. Implies -x.
Es scheint mir, dass alle auf dieser Seite vorhandenen Antworten falsch sind, einschließlich der als richtig markierten. Das liegt daran, dass die Frage mehrdeutig formuliert ist.
Zusammenfassung: Wenn Sie den Befehl "genau einmal für jede angegebene Eingabezeile" ausführen möchten, indem Sie die gesamte Zeile (ohne Newline) an den Befehl als einzelnes Argument übergeben, dann ist dies der beste UNIX-kompatible Weg, dies zu tun:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
verfügt möglicherweise über nützliche Erweiterungen, mit denen Sie tr
abschaffen können. Sie sind jedoch unter OS X und anderen UNIX-Systemen nicht verfügbar.
Nun zur langen Erklärung…
Bei der Verwendung von xargs sind zwei Punkte zu berücksichtigen:
Um das Verhalten von xargs zu testen, benötigen wir ein Dienstprogramm, das anzeigt, wie oft es ausgeführt wird und mit wie vielen Argumenten. Ich weiß nicht, ob es ein Standardprogramm dafür gibt, aber wir können es leicht in bash programmieren:
#!/bin/bash
echo -n "-> "; for a in "[email protected]"; do echo -n "\"$a\" "; done; echo
Angenommen, Sie speichern es als show
in Ihrem aktuellen Verzeichnis und machen es ausführbar, so funktioniert es:
$ ./show one two 'three and four'
-> "one" "two" "three and four"
Wenn es sich bei der ursprünglichen Frage wirklich um Punkt 2 handelt (wie ich denke, nachdem ich sie ein paar Mal gelesen habe), und so ist sie zu lesen (Änderungen in Fettschrift):
Wie kann ich xargs den Befehl genau einmal für jedes Argument der eingegebenen Eingabe ausführen lassen? Das Standardverhalten besteht darin, Eingabe in Argumente zu unterteilen und den Befehl so oft wie möglich auszuführen, wobei an jede Instanz mehrere Argumente übergeben werden.
dann lautet die Antwort -n 1
.
Vergleichen wir das Standardverhalten von xargs, das die Eingabe um Leerzeichen aufteilt und den Befehl so oft wie möglich aufruft:
$ echo one two 'three and four' | xargs ./show
-> "one" "two" "three" "and" "four"
und sein Verhalten mit -n 1
:
$ echo one two 'three and four' | xargs -n 1 ./show
-> "one"
-> "two"
-> "three"
-> "and"
-> "four"
Wenn sich die ursprüngliche Frage dagegen um Punkt 1 handelte, war die Aufteilung der Eingabedateien so zu lesen (viele Leute, die hierher kommen, scheinen zu glauben, dass dies der Fall ist oder die beiden Fragen verwirrt):
Wie kann ich xargs veranlassen, dass der Befehl with genau ein Argument für jede angegebene Eingabezeile ausgeführt wird? Standardmäßig werden die Zeilen um Leerzeichen aufgeteilt.
dann ist die Antwort subtiler.
Man könnte denken, dass -L 1
hilfreich sein könnte, aber es stellt sich heraus, dass das Argument-Parsing nicht geändert wird. Er führt den Befehl nur einmal für jede Eingabezeile aus, mit so vielen Argumenten, wie in dieser Eingabezeile vorhanden waren:
$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show
-> "one"
-> "two"
-> "three" "and" "four"
Nicht nur das, aber wenn eine Zeile mit Leerzeichen endet, wird sie an die nächste angehängt:
$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show
-> "one" "two"
-> "three" "and" "four"
Natürlich geht es bei -L
nicht darum, wie xargs die Eingabe in Argumente aufteilt.
Das einzige Argument, das plattformübergreifend ist (mit Ausnahme der Erweiterungen GNU), ist -0
, das die Eingabe in NUL-Bytes aufteilt.
Dann müssen Sie nur Zeilenumbrüche mit Hilfe von tr
in NUL übersetzen:
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show
-> "one " "two" "three and four"
Nun sieht das Argument Parsen gut aus, einschließlich des nachgestellten Leerraums.
Wenn Sie diese Technik mit -n 1
kombinieren, erhalten Sie genau eine Befehlsausführung pro Eingabezeile, unabhängig von Ihrer Eingabe. Dies ist möglicherweise eine andere Möglichkeit, die ursprüngliche Frage zu betrachten (möglicherweise die intuitivste unter Berücksichtigung des Titels):
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show
-> "one "
-> "two"
-> "three and four"
Wenn Sie den Befehl für die Zeile every (d. H. Ergebnis) ausführen möchten, die von find
stammt, wozu brauchen Sie die xargs
?
Versuchen:
find
Pfad-type f -exec
Ihr-Befehl{} \;
wobei das Literal {}
durch den Dateinamen ersetzt wird und das Literal \;
für find
benötigt wird, um zu wissen, dass der benutzerdefinierte Befehl dort endet.
(Nach der Bearbeitung Ihrer Frage klären Sie, dass Sie -exec
wissen)
Von man xargs
:
-L max-lines
Verwenden Sie höchstens max-lines nicht leere Eingabezeilen pro Befehlszeile. Nachlaufen Leerzeichen bewirken, dass eine Eingabezeile in der nächsten Eingabezeile logisch fortgesetzt wird . Impliziert -x.
Beachten Sie, dass Dateinamen, die in Leerzeichen enden, Probleme verursachen, wenn Sie xargs
verwenden:
$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\ b c\ c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory
Wenn Sie sich also nicht für die -exec
-Option interessieren, verwenden Sie -print0
und -0
besser:
$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a
Wie kann ich xargs veranlassen, dass der Befehl für jede angegebene Eingabezeile genau einmal ausgeführt wird?
Ich verwende -L 1
answer nicht, da es keine Dateien mit Leerzeichen verarbeitet, was eine Schlüsselfunktion von find -print0
ist.
echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: space.txt: No such file or directory
ls: with: No such file or directory
Eine bessere Lösung ist die Verwendung von tr
zum Konvertieren von Zeilenumbrüchen in Nullzeichen (\0
) und das Argument xargs -0
.
echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt
Wenn Sie die Anzahl der Aufrufe begrenzen müssen, können Sie das Argument -n 1
verwenden, um für jede Eingabe einen Aufruf an das Programm zu tätigen:
echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls
Dadurch können Sie auch die Ausgabe von find before umwandeln, um die Unterbrechungen in Nullen umzuwandeln.
find . -name \*.xml | grep -v /workspace/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar
Eine andere Alternative ...
find /path -type f | while read ln; do echo "processing $ln"; done
Diese beiden Möglichkeiten funktionieren auch und funktionieren auch für andere Befehle, die find nicht verwenden!
xargs -I '{}' rm '{}'
xargs -i rm '{}'
beispiel Anwendungsfall:
find . -name "*.pyc" | xargs -i rm '{}
löscht alle pyc-Dateien in diesem Verzeichnis, auch wenn die pyc-Dateien Leerzeichen enthalten.
find path -type f | xargs -L1 command
ist alles, was du brauchst.
Der folgende Befehl sucht alle Dateien (-type f) in /path
und kopiert sie dann mit cp
in den aktuellen Ordner. Beachten Sie die Verwendung von if -I %
, um ein Platzhalterzeichen in der cp
-Befehlszeile anzugeben, damit nach dem Dateinamen Argumente eingefügt werden können.
find /path -type f -print0 | xargs -0 -I % cp % .
Getestet mit xargs (GNU findutils) 4.4.0
Sie können die Anzahl der Zeilen oder Argumente (wenn zwischen den einzelnen Argumenten Leerzeichen stehen) begrenzen, indem Sie die Flags --max-lines bzw. --max-args verwenden.
-L max-lines Use at most max-lines nonblank input lines per command line. Trailing blanks cause an input line to be logically continued on the next input line. Implies -x. --max-lines[=max-lines], -l[max-lines] Synonym for the -L option. Unlike -L, the max-lines argument is optional. If max-args is not specified, it defaults to one. The -l option is deprecated since the POSIX standard specifies -L instead. --max-args=max-args, -n max-args Use at most max-args arguments per command line. Fewer than max-args arguments will be used if the size (see the -s option) is exceeded, unless the -x option is given, in which case xargs will exit.
Die Antworten von @Draemon scheinen mit "-0" richtig zu sein, auch wenn in der Datei Speicherplatz vorhanden ist.
Ich habe den Befehl xargs ausprobiert und habe festgestellt, dass "-0" perfekt mit "-L" funktioniert. Sogar die Leerzeichen werden behandelt (wenn die Eingabe mit Null beendet wurde). Folgendes ist ein Beispiel:
#touch "file with space"
#touch "file1"
#touch "file2"
Im Folgenden werden die Nullen aufgeteilt und der Befehl für jedes Argument in der Liste ausgeführt:
#find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2
-L1
führt also das Argument für jedes mit Null abgeschlossene Zeichen aus, wenn es mit "-0" verwendet wird. Um den Unterschied zu sehen, versuchen Sie:
#find . -name 'file*' -print0 | xargs -0 | xargs -L1
./file with space ./file1 ./file2
selbst das wird einmal ausgeführt:
#find . -name 'file*' -print0 | xargs -0 | xargs -0 -L1
./file with space ./file1 ./file2
Der Befehl wird einmal ausgeführt, da das "-L" jetzt nicht auf null Byte aufgeteilt wird. Sie müssen "-0" und "-L" angeben, um zu funktionieren.
Es scheint, dass ich nicht genug Ruf habe, um einen Kommentar zu Tobias Antwort oben hinzuzufügen. Daher füge ich diese "Antwort" hinzu, um denjenigen von uns zu helfen, die auf den Windows-Plattformen auf die gleiche Weise mit xargs
experimentieren möchten.
Hier ist eine Windows-Batchdatei, die dasselbe wie das schnell codierte "show" -Skript von Tobia ausführt:
@echo off
REM
REM cool trick of using "set" to echo without new line
REM (from: http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
exit /b
)
<nul set /p=Args: "%~1"
shift
:start
if not "%~1" == "" (
<nul set /p=, "%~1"
shift
goto start
)
echo.