Wie führe ich eine Schleife über die Zeilen in STDIN aus und führe einen Shell-Befehl aus?

Wie führe ich eine Schleife über die Zeilen in STDIN aus und führe einen Shell-Befehl aus?

Ich möchte für jede von STDIN übernommene Zeile einen Shell-Befehl ausführen.

In diesem Fall möchte ich ausführen xargs mv. Als Beispiel seien zwei Zeilen angegeben:

mfoo foo
mbar bar

Ich möchte ausführen:

xargs mv mfoo foo
xargs mv mbar bar

rubyIch habe die folgenden Strategien mit , awk, und ausprobiert xargs. Allerdings mache ich es falsch:

Nur xargs:

$ echo "mbar bar\nmbaz baz" | xargs mv
usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory

Durch awk:

$ echo "mbar bar\nmbaz baz" | awk '{ system("xargs $0") }'

Durch ruby:

$ echo "mbar bar\nmbaz baz" | ruby -ne '`xargs mv`'
$ ls
cat  foo  mbar mbaz

Ich habe ein paar Fragen:

  • Wie mache ich das, was ich versuche?
  • Was ist an jedem meiner Versuche falsch?
  • Gibt es eine bessere Möglichkeit, darüber nachzudenken, was ich versuche zu tun?

Was mich besonders verwirrt, ist, dass mein xargsVersuch nicht funktioniert, denn Folgendes funktioniert:

$ echo "foo\nbar" | xargs touch
$ ls
bar foo

Antwort1

echo "mbar bar\nmbaz baz" | xargs mv

Mit xargssollten Sie die Option verwenden, -tum zu sehen, was passiert. Wenn wir also in Ihrem obigen Fall xargsmit aufrufen würden -t, was sehen wir:

mv mbar bar mbaz baz

Das ist also offensichtlich nicht richtig. Was passiert ist, war, dass es xargswie ein hungriges Krokodil alle Argumente gefressen hat, die ihm über die Pipe zugeführt wurden echo. Sie brauchen also eine Möglichkeit, die Weitergabe von Argumenten an das Krokodil zu begrenzen. Und da Sie zeilenweise angefordert haben, benötigen Sie die Option -loder die -LOption für POSIX.

echo "mbar bar\nmbaz baz" | xargs -l -t mv

POSIX-artiger Weg:

echo "mbar bar\nmbaz baz" | xargs -L 1 -t mv

mv mbar bar
mv mbaz baz

Und das ist, was Sie wollten. HTH

Antwort2

Sie können diesen Weg gehen:

echo -e "foo bar\ndoo dar" | xargs -n 2 mv

So lesen Sie aus einer Datei:

cat lines.txt | xargs -n 2 mv

oder

xargs -a lines.txt -n 2 mv

Antwort3

xargsverhält sich möglicherweise nicht wie erwartet. Siehe @MichaelHomers Kommentar zu dieser Frage:

xargs mv mfoo foowartet auf Eingabe und führt mv mfoo foo $x_1 $x_2 $x_3...für jede $x_nEingabezeile aus, die es erhält. @MichaelHomer

Dies kann durch das Lesen derxargsmanpage:

BESCHREIBUNG
Das Dienstprogramm xargs liest durch Leerzeichen, Tabulatoren, Zeilenumbrüche und Dateiende getrennte Zeichenfolgen von der Standardeingabe und führt das Dienstprogramm mit den Zeichenfolgen als Argumente aus.

In der Praxis scheint es so, als ob xargsZeilenumbrüche ignoriert werden, wenn dem Befehlszeilenprogramm Argumente zugeführt werden, die als erstes Argument angegeben sind. Beachten Sie beispielsweise, was mit dem Zeilenumbruch passiert:

$ echo "foo bar\ncat dog"
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs echo
foo bar cat dog

Dieses Verhalten kann gesteuert werden mit dem-nFlagge:

-n Zahl

Legen Sie die maximale Anzahl von Argumenten fest, die bei jedem Aufruf des Dienstprogramms von der Standardeingabe übernommen werden
.

Wenn wir zum vorherigen Beispiel zurückkehren und xargsnur zwei Argumente übernehmen und dann beenden möchten, können wir Folgendes verwenden:@ddnomads Ansatz:

$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog

Wie aus den Kommentaren in Rakesh Sharmas Antwort hervorgeht, xargskönnen Sie unter BSD außerdem die Anzahl der zu lesenden Zeilen mit dem -LFlag angeben. Aus der Manpage:

-L Zahl

Rufen Sie das Dienstprogramm für jede gelesene Zeilenanzahl auf. Wenn EOF erreicht ist und
weniger Zeilen als Anzahl gelesen wurden, wird das Dienstprogramm mit den verfügbaren Zeilen aufgerufen.

Ebenso hilfreich zum Testen ist der -tSchalter:

Wenn wir unser Beispiel noch einmal betrachten, werden beide der folgenden Möglichkeiten funktionieren. Das zweite Beispiel ist jedoch besser, da es das ist, was in diesem Fall beabsichtigt war:

$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs -L 1 echo
foo bar
cat dog

Hinweis aus der Manpage:

Die Optionen -L und -n schließen sich gegenseitig aus; es wird die zuletzt angegebene Option verwendet.

Antwort4

Es gibt eine sehr einfache Lösung, die keine xargs verwendet.
Definieren Sie diese Funktion:

$ mv2() { while (($# >1 )); do echo mv "$1" "$2"; shift 2; done; }

Rufen Sie es dann einfach mit der Liste der benötigten Dateinamen auf (keine Zeilenumbrüche erforderlich):

$ mv2 mbar bar mbaz baz
mv mbar bar
mv mbaz baz

Entfernen Sie das Echo, damit die Funktion wirklich funktioniert.

verwandte Informationen