xargs - füge jedem Argument einen Parameter hinzu

12

Ich weiß, dass gegeben l="a b c",

echo $l | xargs ls

Ausbeuten

ls a b c

Welches Konstrukt ergibt

mycommand -f a -f b -f c
kein Benutzer
quelle

Antworten:

12

Ein Weg, es zu tun:

echo "a b c" | xargs printf -- '-f %s\n' | xargs mycommand

Dies setzt voraus a, bund centhält keine Leerzeichen, Zeilenumbrüche, Anführungszeichen oder Backslashes. :)

Mit GNU können findutilSie den allgemeinen Fall behandeln, aber es ist etwas komplizierter:

echo -n "a|b|c" | tr \| \\0 | xargs -0 printf -- '-f\0%s\0' | xargs -0 mycommand

Sie können den ersetzen |Separator mit einem anderen Zeichen, dass nicht angezeigt wird in a, boder c.

Bearbeiten: Wie @MichaelMol feststellt, besteht bei einer sehr langen Liste von Argumenten die Gefahr, dass die maximale Länge der Argumente, an die übergeben werden kann, überschritten wird mycommand. In diesem Fall xargswird die Liste beim letzten Mal geteilt und eine weitere Kopie von ausgeführt mycommand. Es besteht die Gefahr, dass die Liste nicht abgeschlossen wird -f. Wenn Sie sich wegen dieser Situation Sorgen machen, können Sie die letzte xargs -0durch Folgendes ersetzen :

... | xargs -x -0 mycommand

Dies wird das Problem nicht lösen, aber die Ausführung wird abgebrochen, mycommandwenn die Liste der Argumente zu lang wird.

Satō Katsura
quelle
1
Sie laufen ein ziemlich hässliches Risiko ARG_MAX, einen -fvon seinem gepaarten Parameter getrennten Wert zu überschreiten und zu haben .
Michael Mol
@MichaelMol Das ist ein guter Punkt, aber ich glaube nicht, dass es eine sinnvolle Möglichkeit gibt, mit dieser Situation umzugehen, ohne mehr darüber zu wissen mycommand. Sie könnten immer -xzum letzten hinzufügen xargs.
Satō Katsura
Ich denke, die richtige Lösung ist wahrscheinlich überhaupt nicht zu verwenden xargsund nur zu verwenden, findwenn es verwendet werden kann. Diese Lösung ist gefährlich; Sie sollten in Ihrer Antwort zumindest vor dem Fehlerfall warnen.
Michael Mol
@MichaelMol Ich sehe wirklich keine findbessere allgemeine Lösung, insbesondere wenn die anfänglichen Argumente keine Dateinamen sind. :)
Satō Katsura
Wir wissen nicht, was die anfänglichen Argumente sind; Wir sehen nur das gegebene Beispiel, nicht das Szenario, das die Frage inspiriert hat. Intuition legt nahe, dass sich @ not-a-user mit einem Argument namens -fund einem Beispieltool lszur Veranschaulichung mit Dateinamen befasst. Und da findbietet das -execArgument, das Ihnen erlaubt zu konstruieren eine Befehlszeile, es ist in Ordnung. (Solange mycommanderlaubt ist, mehr als einmal auszuführen. Wenn es nicht ist, dann haben wir ein anderes Problem mit der Verwendung von xargshier ...)
Michael Mol
5

Ein besserer Weg, um es anzugehen (IMO) wäre:

  • in zsh:

    l=(a b c)
    mycommand -f$^l

    oder mit Array-Zipping, damit das Argument nicht an die Option angehängt wird:

    l=(a b c) o=(-f)
    mycommand "${o:^^l}"

    Auf diese Weise funktioniert es immer noch, wenn das lArray leere Elemente oder Elemente enthält, die Leerzeichen oder andere problematische Zeichen für enthalten xargs. Beispiel:

    $ l=(a '' '"' 'x y' c) o=(-f)
    $ printf '<%s>\n' "${o:^^l}"
    <-f>
    <a>
    <-f>
    <>
    <-f>
    <">
    <-f>
    <x y>
    <-f>
    <c>
  • in rc:

    l=(a b c)
    mycommand -f$l
  • in fish:

    set l a b c
    mycommand -f$l

(AFAIK rcund fishkein Array-Zippen)

Mit Bourne-ähnlichen Shells im alten Stil bashkönnten Sie immer Folgendes tun (und trotzdem jedes Zeichen in den Elementen des $@Arrays zulassen):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"
Stéphane Chazelas
quelle
1
Eine andere Möglichkeit, dies in bash zu tun, ist die Verwendung einer benannten Arrayvariablen. for i; do args+=('-f' "$i");done; mycommand "${args[@]}". IDK, wenn dies schneller ist, aber das Anhängen von 2 Elementen an ein Array wie O (n) erscheint, während Ihre setSchleife wahrscheinlich jedes Mal die akkumulierte Arg-Liste kopiert und erneut analysiert (O (n ^ 2)).
Peter Cordes