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
ruby
Ich 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 xargs
Versuch nicht funktioniert, denn Folgendes funktioniert:
$ echo "foo\nbar" | xargs touch
$ ls
bar foo
Antwort1
echo "mbar bar\nmbaz baz" | xargs mv
Mit xargs
sollten Sie die Option verwenden, -t
um zu sehen, was passiert. Wenn wir also in Ihrem obigen Fall xargs
mit aufrufen würden -t
, was sehen wir:
mv mbar bar mbaz baz
Das ist also offensichtlich nicht richtig. Was passiert ist, war, dass es xargs
wie 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 -l
oder die -L
Option 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
xargs
verhält sich möglicherweise nicht wie erwartet. Siehe @MichaelHomers Kommentar zu dieser Frage:
xargs mv mfoo foo
wartet auf Eingabe und führt mvmfoo foo $x_1 $x_2 $x_3...
für jede$x_n
Eingabezeile aus, die es erhält. @MichaelHomer
Dies kann durch das Lesen derxargs
manpage:
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 xargs
Zeilenumbrü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-n
Flagge:
-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 xargs
nur 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, xargs
können Sie unter BSD außerdem die Anzahl der zu lesenden Zeilen mit dem -L
Flag 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 -t
Schalter:
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.