Einen Befehl einmal pro Zeile der weitergeleiteten Eingabe ausführen?

162

Ich möchte einen Java-Befehl einmal für jedes Match von ausführen ls | grep pattern -. In diesem Fall, denke ich, könnte ich tun, find pattern -exec java MyProg '{}' \;aber ich bin neugierig auf den allgemeinen Fall - gibt es eine einfache Möglichkeit zu sagen: "Führen Sie einen Befehl einmal für jede Zeile der Standardeingabe aus"? (In Fisch oder Bash.)

Xodarap
quelle

Antworten:

92

Das ist was xargstut.

... | xargs command
Keith
quelle
25
Nicht ganz. printf "foo bar\nbaz bat" | xargs echo wheewird nachgeben whee foo bar baz bat. Vielleicht die -Loder -nOptionen hinzufügen ?
Jander
3
@Jander Die Frage war eher allgemein, also habe ich das allgemeine Tool gegeben. Richtig, Sie müssen das Verhalten mit Optionen an die jeweiligen Umstände anpassen.
Keith
4
... | tr '\ n' '\ 0' | xargs -0
vrdhn
7
wie "die spezifischen Umstände, die die richtige Antwort auf die Frage geben". :)
mattdm
7
Wenn Sie den richtigen Weg sehen möchten, dies mit xargs zu tun, finden Sie meine Antwort unten.
Michael Goldshteyn
167

Die akzeptierte Antwort hat die richtige Idee, aber der Schlüssel ist, xargsden -n1Schalter zu übergeben, was bedeutet, dass der Befehl einmal pro Ausgabezeile ausgeführt wird:

cat file... | xargs -n1 command

Oder für eine einzelne Eingabedatei können Sie die Pipe catganz vermeiden und einfach mit gehen:

<file xargs -n1 command
Michael Goldshteyn
quelle
1
Von Interesse ist auch die Möglichkeit xargs, nicht ausgeführt zu werden, wenn stdinleer ist --no-run-if-empty -r:: Wenn die Standardeingabe keine Nicht-Leerzeichen enthält, führen Sie den Befehl nicht aus. Normalerweise wird der Befehl einmal ausgeführt, auch wenn keine Eingabe erfolgt. Diese Option ist eine GNU-Erweiterung.
Ronan Jouchet
4
Wie kommst du in die Leitung command?
BT
Dies ist die korrekte Verwendung von Xargs. Ohne -n1 funktioniert es nur bei Befehlen, die Listen von Parametern als Mehrfachaufrufe behandeln, die nicht alle ausführen.
Masterxilo
3
printf "foo bar \ nbaz Fledermaus" | xargs -n1 echo whee spaltet sich durch Worte und nicht durch Zeilen
Gismo Ranas
112

In Bash oder einer anderen Bourne-Shell (ash, ksh, zsh,…):

while read -r line; do command "$line"; done

read -rLiest eine einzelne Zeile von der Standardeingabe ( readohne -rinterpretierte Backslashes, das wollen Sie nicht). Somit können Sie eine der folgenden Aktionen ausführen:

$ command | while read -r line; do command "$line"; done  

$ while read -r line; do command "$line"; done <file
Steven D
quelle
6
Als ich es versuchte tail -f syslog | grep -e something -e somethingelse| while read line; do echo $line; done, funktionierte es nicht. Es funktionierte mit einer Datei, die in die whileSchleife geleitet wurde. Es funktionierte nur tail -fmit der grep, aber nicht mit beiden Pipes. Die Angabe grepder --line-bufferedOption machte es Arbeit
Dies funktioniert auch, wenn jede Zeile an stdin gesendet werden muss:command | while read -r line; do echo "$line" | command ; done
Den
21

Ich stimme Keith zu, xargs ist das allgemeinste Werkzeug für diesen Job.

Normalerweise benutze ich einen dreistufigen Ansatz.

  • Machen Sie die grundlegenden Dinge, bis Sie etwas haben, mit dem Sie arbeiten möchten
  • Bereiten Sie die Zeile mit awk vor, damit sie die richtige Syntax erhält
  • dann lass xargs es ausführen, vielleicht mit hilfe von bash.

Es gibt kleinere und schnellere Wege, aber diese Wege funktionieren fast immer.

Ein einfaches Beispiel:

ls | 
grep xls | 
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"\0"}' | 
xargs -0 bash -c

In den ersten beiden Zeilen werden einige Dateien ausgewählt, mit denen gearbeitet werden soll. Anschließend bereitet awk einen schönen String mit einem auszuführenden Befehl und einigen Argumenten vor. $ 1 ist die erste Spalteneingabe aus der Pipe. Und schließlich stelle ich sicher, dass xargs diesen String an bash sendet, der ihn gerade ausführt.

Es ist ein bisschen übertrieben, aber dieses Rezept hat mir an vielen Stellen geholfen, da es sehr flexibel ist.

Johan
quelle
6
Beachten Sie, dass xargs -0das Null-Byte als Trennzeichen für printf("MyJavaProg --args \"%s\"\0",$1)
Datensätze
@glenn: Verpasste das Nullzeichen, wird die Antwort aktualisieren
Johan
@Johan keine große Sache, aber wenn Sie verwenden awk, können Sie es das Muster Match tun und überspringen die grep zBls | awk '/xls/ {print...
Eric Renouf
15

GNU Parallel ist für diese Art von Aufgaben gemacht. Die einfachste Verwendung ist:

cat stuff | grep pattern | parallel java MyProg

Sehen Sie sich das Intro-Video an, um mehr zu erfahren: http://www.youtube.com/watch?v=OpaiGYxkSuQ

Ole Tange
quelle
1
Keine wirkliche Notwendigkeit für die cathier, da grepkann direkt die Datei lesen
Eric Renouf
1
Vielen Dank für den Link, ich stimme nicht unbedingt zu, dass es einfacher zu lesen ist, aber es ist gut zu wissen, dass es trotzdem in Betracht gezogen wurde. Ich würde nur jetzt ein wenig darüber streiten, dass der Link hier eigentlich nicht zutrifft, da die Alternative nicht wirklich ist, < stuff grep patternaber grep pattern stuffohne Umleitung oder Katze überhaupt benötigt wird. Das ändert jedoch nichts an Ihrem Argument und wenn Sie der Meinung sind, dass es klarer ist, Dinge immer in einer Pipe zu verwenden, die mit beginnt cat, dann haben Sie die Macht
Eric Renouf,
8

Auch eine while readSchleife in der Fischschale (ich nehme an, Sie möchten eine Fischschale, wenn Sie ein ).

command | while read line
    command $line
end

Einige Punkte zu beachten.

  • readEs werden keine -rArgumente verwendet und keine umgekehrten Schrägstriche interpretiert, um den häufigsten Anwendungsfall zu vereinfachen.
  • Sie müssen nicht zitieren $line, da Fische im Gegensatz zu bash Variablen nicht durch Leerzeichen trennen.
  • commandan sich ist ein Syntaxfehler (um die Verwendung von Platzhalterargumenten abzufangen). Ersetzen Sie es durch den echten Befehl.
Konrad Borowski
quelle
Muss nicht whilemit do& gepaart werden doneanstatt end?
Aff
@aff Hier geht es speziell um Fischschalen, die unterschiedliche Syntax haben.
Konrad Borowski
Ah, das ist es, was der Fisch bedeutet.
Aff
6

Wenn Sie steuern müssen, wo genau das Eingabeargument in Ihre Befehlszeile eingefügt wird, oder wenn Sie es mehrmals wiederholen müssen, müssen Sie verwenden xargs -I{}.

BEISPIEL 1

Erstellen Sie eine leere Ordnerstruktur another_folder, die die Unterordner im aktuellen Verzeichnis widerspiegelt:

    ls -1d ./*/ | xargs -I{} mkdir another_folder/{}
BEISPIEL 2

Wenden Sie eine Operation auf eine Dateiliste von stdin an. Erstellen Sie in diesem Fall eine Kopie jeder .htmlDatei, indem Sie eine .bakErweiterung anhängen :

    find . -iname "*.html" | xargs -I{} cp {} {}.bak

Von der xargsManpage für MacOS / BSD :

 -I replstr
         Execute utility for each input line, replacing one or more occurrences of
         replstr in up to replacements (or 5 if no -R flag is specified) arguments
         to utility with the entire line of input.  The resulting arguments, after
         replacement is done, will not be allowed to grow beyond 255 bytes; this is
         implemented by concatenating as much of the argument containing replstr as
         possible, to the constructed arguments to utility, up to 255 bytes.  The
         255 byte limit does not apply to arguments to utility which do not contain
         replstr, and furthermore, no replacement will be done on utility itself.
         Implies -x.

Linux- xargsManpage :

   -I replace-str
          Replace  occurrences of replace-str in the initial-
          arguments with names read from standard input.  Al
          so,  unquoted  blanks do not terminate input items;
          instead the separator  is  the  newline  character.
          Implies -x and -L 1.
ccpizza
quelle
1

Wenn ich mit potenziell nicht bereinigten Eingaben zu tun habe, möchte ich, dass der gesamte Auftrag zeilenweise zur visuellen Überprüfung "ausgeschrieben" wird, bevor ich ihn ausführe (insbesondere, wenn er destruktiv ist, beispielsweise wenn die Postfächer von Personen gesäubert werden).

Ich erstelle also eine Liste von Parametern (z. B. Benutzernamen) und füttere sie zeilenweise in eine Datei:

johndoe  
jamessmith  
janebrown  

Dann öffne ich die Liste in vimund zerfleische sie mit Suchen und Ersetzen-Ausdrücken, bis ich eine Liste der vollständigen Befehle erhalte, die ausgeführt werden müssen, wie folgt:

/bin/rm -fr /home/johndoe  
/bin/rm -fr /home/jamessmith 

Auf diese Weise können Sie bei unvollständigem regulären Ausdruck feststellen, bei welchem ​​Befehl potenzielle Probleme auftreten (z. B. /bin/rm -fr johnnyo connor). Auf diese Weise können Sie Ihren regulären Ausdruck rückgängig machen und es mit einer zuverlässigeren Version erneut versuchen. Die Namensverfälschung ist dafür berüchtigt, weil es schwierig ist, sich um alle Randfälle wie Van Gogh, O'Connors, St. Clair und Smith-Wesson zu kümmern.

Nachdem set hlsearchist dies dafür nützlich vim, da es alle Spiele werden markieren, so dass Sie leicht erkennen , wenn es nicht in unbeabsichtigter Weise oder Streichhölzer überein.

Sobald Ihre Regex perfekt ist und alle Fälle erfasst, auf die Sie testen / denken können, konvertiere ich sie normalerweise in einen sed-Ausdruck, damit sie für einen weiteren Durchlauf vollständig automatisiert werden kann.

In Fällen, in denen die Anzahl der Eingabezeilen eine visuelle Überprüfung verhindert, empfehle ich dringend, den Befehl vor der Ausführung auf dem Bildschirm (oder besser noch in einem Protokoll) wiederzugeben. Wenn ein Fehler auftritt, wissen Sie genau, welcher Befehl verursacht wurde es zu scheitern. Dann können Sie zu Ihrem ursprünglichen regulären Ausdruck zurückkehren und ihn erneut anpassen.

Marcin
quelle
0

Wenn ein Programm die Pipe ignoriert, aber Dateien als Argumente akzeptiert, können Sie es einfach auf die spezielle Datei verweisen /dev/stdin.

Ich bin nicht mit Java vertraut, aber hier ist ein Beispiel, wie Sie es für Bash tun würden:

$ echo $'pwd \n cd / \n pwd' |bash /dev/stdin
/home/rolf
/

Das $ ist notwendig, damit Bash \nin Zeilenumbrüche übersetzt werden kann. Ich bin mir nicht sicher warum.

Rolf
quelle
0

Ich bevorzuge dies - mehrzeilige Befehle und eindeutigen Code zulassen

find -type f -name filenam-pattern* | while read -r F
do
  echo $F
  cat $F | grep 'some text'
done

Siehe https://stackoverflow.com/a/3891678/248616

Nam G VU
quelle
0

Hier eine Copypaste, die Sie sofort verwenden können:

cat list.txt | xargs -I{} command parameter {} parameter

Das Element aus der Liste wird dort abgelegt, wo sich das {} befindet, und der Rest des Befehls und der Parameter werden unverändert verwendet.

DustWolf
quelle