xargs - ajoute chaque argument avec un paramètre

12

Je sais que, étant donné l="a b c",

echo $l | xargs ls

les rendements

ls a b c

Quelle construction donne

mycommand -f a -f b -f c
pas un utilisateur
la source

Réponses:

13

Une façon de le faire:

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

Cela suppose a, bet cne contient pas de blancs, de nouvelles lignes, de guillemets ou de barres obliques inverses. :)

Avec GNU, findutilvous pouvez gérer le cas général, mais c'est un peu plus compliqué:

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

Vous pouvez remplacer le |séparateur par un autre caractère, qui ne semble pas a, bou c.

Edit: Comme le note @MichaelMol , avec une très longue liste d'arguments, il existe un risque de débordement de la longueur maximale des arguments qui peuvent être passés mycommand. Lorsque cela se produit, le dernier xargsdivisera la liste et exécutera une autre copie de mycommand, et il y a un risque qu'elle ne soit pas terminée -f. Si vous vous inquiétez de cette situation, vous pouvez remplacer le dernier xargs -0ci-dessus par quelque chose comme ceci:

... | xargs -x -0 mycommand

Cela ne résoudra pas le problème, mais interrompra l'exécution mycommandlorsque la liste des arguments devient trop longue.

Satō Katsura
la source
1
Vous courez un risque assez laid de dépasser ARG_MAXet d'avoir un -fparamètre séparé de son paramètre apparié.
Michael Mol
@MichaelMol C'est un bon point, mais je ne pense pas qu'il existe un moyen significatif de gérer cette situation sans en savoir plus mycommand. Vous pouvez toujours ajouter -xau dernier xargs.
Satō Katsura
Je pense que la bonne solution est probablement de ne pas utiliser xargsdu tout, et de l'utiliser uniquement findsi elle peut être utilisée. Cette solution est dangereuse; vous devez au moins avertir du cas d'échec dans votre réponse.
Michael Mol
@MichaelMol Je ne vois vraiment pas comment findserait une meilleure solution générale, en particulier lorsque les arguments initiaux ne sont pas des noms de fichiers. :)
Satō Katsura
Nous ne savons pas quels sont les arguments initiaux; nous ne voyons que l'exemple donné, pas le scénario qui a inspiré la question. L'intuition suggère qu'avec un argument nommé -fet avec un exemple d'outil lsutilisé pour l'illustration, @ not-a-user traite les noms de fichiers. Et donné findoffre l' -execargument, qui vous permet de construire une ligne de commande, ça va. (Tant qu'il mycommandest permis d'exécuter plus d'une fois. Si ce n'est pas le cas, alors nous avons un autre problème avec l'utilisation d' xargsici ...)
Michael Mol
5

Une meilleure façon d'y remédier (OMI) serait:

  • dans zsh:

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

    ou en utilisant le zipping de tableau pour que l'argument ne soit pas attaché à l'option:

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

    De cette façon, cela fonctionne toujours si le ltableau contient des éléments vides ou des éléments contenant des espaces ou tout autre caractère problématique pour xargs. Exemple:

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

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

    set l a b c
    mycommand -f$l

(AFAIK, rcet fishn'ont pas de fermeture éclair de tableau)

Avec des shells de type Bourne à l'ancienne bash, vous pouvez toujours le faire (en autorisant toujours n'importe quel caractère dans les éléments du $@tableau):

set -- a b c
for i do set -- "$@" -f "$i"; shift; done
mycommand "$@"
Stéphane Chazelas
la source
1
Une autre façon de le faire en bash est avec une variable de tableau nommée. for i; do args+=('-f' "$i");done; mycommand "${args[@]}". IDK si c'est plus rapide, mais l'ajout de 2 éléments à un tableau semble être O (n), tandis que votre setboucle copie et ré-analyse probablement la liste d'arguments accumulée à chaque fois (O (n ^ 2)).
Peter Cordes