Passer plusieurs paramètres via xargs

17

J'aimerais pouvoir utiliser xargspour exécuter plusieurs paramètres dans différentes parties d'une commande.

Par exemple, les éléments suivants:

echo {1..8} | xargs -n2 | xargs -I v1 -I v2 echo the number v1 comes before v2

J'espère que ça reviendra

the number 1 comes before 2
the number 3 comes before 4 

... etc

Est-ce réalisable? Je soupçonne que mon utilisation multiple de -Iest incorrecte.

Damien Sawyer
la source

Réponses:

29

Je crois que vous ne pouvez pas utiliser de -Icette façon. Mais vous pouvez obtenir l'effet / le comportement souhaité en disant:

echo {1..8} | xargs -n2 sh -c 'echo "the number $1 comes before $2"' sh

Cela crée essentiellement un script shell d'une ligne ad hoc , qui xargss'exécute via sh -c. Les deux valeurs qui xargsanalysent l'entrée sont passées à ce «script». Le shell attribue ensuite ces valeurs à $1et $2, que vous pouvez ensuite référencer dans le «script».

Scott
la source
4
Merci - c'est super. J'ai cependant lu l'homme pour sh et j'ai du mal à comprendre ce que fait le deuxième appel à sh et, par extension, pourquoi son omission donne «la moitié» du résultat.
Damien Sawyer
6
@DamienSawyer Il n'y a pas de deuxième appel à sh. La fin shà la fin est ce qui est mis $0. $0est généralement ce qui contient le nom de l'interpréteur ou du script.
Kusalananda
Je viens de remplacer le suivi shavec echo $shlequel cela fonctionne. Donc finale shfonctionne comme un espace réservé
kenn
Eh bien, c'est une simplification excessive de ce que Kusalananda a dit.
Scott
6

Dans le cas particulier de printf, vous pouvez toujours faire:

echo {1..8} | xargs printf 'the number %s comes before %s\n'

car printfa une xargscapacité intrinsèque à s'exécuter plusieurs fois si on lui donne plus d'arguments que nécessaire pour une seule invocation. Bien que cela ait peu d'avantages par rapport à

printf 'the number %s comes before %s\n' {1..8}

Et pour les grandes listes, la xargscommande simple peut entraîner l' xargsexécution de plusieurs instances de printf, dont certaines peuvent avoir un nombre impair d'arguments. Vous pouvez passer -n 1000à xargspour vous prémunir contre cela, où 1000 est un nombre pair qui devrait être suffisamment petit pour ne pas atteindre la limite d' argument trop longue et suffisamment grande pour éviter d'exécuter autant de printfs.

Notez que ce xargsserait appeler, non pas le shell de votre shell printf, mais l'externe printf, à chaque invocation dans un nouveau processus distinct.

Notez également que pour une entrée vide, sauf sur certains BSD, elle fonctionnerait toujours printfune fois sans argument. GNU xargset compatible ont une option -r (ou --no-run-if-empty) pour éviter cela.

Pour être clair, cette réponse simple est spécifique à votre printfexemple et ne fonctionnerait pas dans le cas général où vous devez passer deux paramètres à la fois à votre commande (comme ce serait le cas diffpar exemple). Pour résoudre le problème général avec zsh, vous pouvez utiliser:

for i j ({1..8}) echo "the number $i comes before $j"
Stéphane Chazelas
la source
(1) À mon humble avis, la zshpartie de ce qui précède est une vraie réponse, et la partie xargs-sans aucune option n'est qu'une bizarrerie. Dans un tel cas, je considérerais de mettre la vraie réponse en premier. (2) Je suppose que vous avez une raison de ne pas utiliser de guillemets dans votre zshcommande. Si c'est le cas, vous voudrez peut-être le dire, de peur que les gens ne lisent ce qui précède et commencent à penser que les citations ne sont pas importantes. (Ou incluez-les simplement, car ils ne font pas de mal.)
Scott
C'est vraiment cool. J'ai atterri ici à la recherche de quelque chose de similaire où je n'étais pas sûr à l'avance du nombre d'arguments que j'allais recevoir qui devaient être mis dans la ligne de commande de l'argument suivant. a fait quelque chose commeawk -F'\t' '/match/{printf $1"\0"$2"\0"}' | xargs -0 printf -- '-args1 "%s" -args2 "%s"' | xargs mycommand
keithpjolley
@keithpjolley, votre utilisation de -0la première xargspour la rendre plus fiable est vaincue si vous ne l'utilisez pas sur la seconde. De plus, le premier argument de printf(à la fois l'utilitaire autonome et awkla printf()fonction de) est le format, vous ne devriez pas y utiliser de variables. Ici, tu pourrais faire awk -F'\t' '/match/{printf "-args1\0%s\0-args2\0%s\0", $1, $2}' | xargs -n400 -r0 mycommand. Encore une fois, -n400nécessaire pour garantir la xargstransmission d'un certain nombre d'arguments qui est un multiple de 4. (notez que toutes les awkimplémentations ne prennent pas en charge l'utilisation de ce \0type ici).
Stéphane Chazelas
-0Je ne l'ai pas utilisé pour le rendre plus fiable, je l'ai utilisé pour qu'il fasse ce que je voulais faire, ce qu'il fait.
keithpjolley
2

essaye ça:

echo {1..8} |xargs -n 2 bash -c 'echo "the number $0 comes before $1"'
md11235
la source
2
Cela fonctionnerait dans ce cas, mais il est préférable d'exécuter le sh -cscript avec sh(ou autre) dans $0. Notez que cela $0n'est pas inclus dans $@si cela devait être utilisé par le sh -cscript, par exemple si le script étaitecho "the two numbers were $@"
Kusalananda