Appel de plusieurs commandes via xargs

cat a.txt | xargs -I % echo %

Dans l'exemple ci-dessus xargs prend echo % comme argument de la commande. Mais dans certains cas, j'ai besoin de plusieurs commandes à traiter au lieu d'une, par exemple:

cat a.txt | xargs -I % {command1; command2; ... }

Mais xargs n'accepte pas ce formulaire. Une solution que je connais est que je peux définir une fonction pour envelopper les commandes, mais ce n'est pas un pipeline, Je ne le préfère pas. Est-il d'autre solution?

227
demandé sur codeforester 2011-08-05 19:22:14

10 réponses

cat a.txt | xargs -I % sh -c 'command1; command2; ...'

Notez qu'il s'agit d'une utilisation inutile de cat . Je l'écrirais comme:

< a.txt xargs -I % sh -c 'command1; command2; ...'

(Oui, la redirection peut être au début de la commande.)

Vraisemblablement command1 et / ou command2 contiendra un ou plusieurs caractères %; sinon, l'option -I % de xargs ne servirait à rien.

336
répondu Keith Thompson 2013-08-23 22:35:39

Avec GNU Parallel, vous pouvez faire:

cat a.txt | parallel 'command1 {}; command2 {}; ...; '

Regardez les vidéos d'introduction pour en savoir plus: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

26
répondu Ole Tange 2016-09-16 07:19:54

C'est juste une autre approche sans xargs ni chat:

while read stuff; do
  command1 "$stuff"
  command2 "$stuff"
  ...
done < a.txt
22
répondu hmontoliu 2017-12-14 09:27:53

Une chose que je fais est d'ajouter à .bashrc/.profilez cette fonction:

function each() {
    while read line; do
        for f in "$@"; do
            $f $line
        done
    done
}

Ensuite, vous pouvez faire des choses comme

... | each command1 command2 "command3 has spaces"

Qui est moins verbeux que xargs ou-exec. Vous pouvez également modifier la fonction pour insérer la valeur de la lecture à un emplacement arbitraire dans les commandes à chacune, si vous avez également besoin de ce comportement.

14
répondu mwm 2012-09-28 01:24:22

Vous pouvez utiliser

cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'

{} = variable pour chaque ligne du fichier texte

11
répondu Ossama 2014-02-14 05:20:17

Une Autre solution qui fonctionne pour moi est quelque chose comme -

cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash

Notez le 'bash' à la fin-je suppose qu'il est passé comme argv [0] à bash. Sans cela dans cette syntaxe, le premier paramètre de chaque commande est perdu. Il peut être n'importe quel mot.

Exemple:

cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " $@; echo "data again: " $@' bash
5
répondu tavvit 2014-02-12 07:54:56

Je préfère le style qui permet le mode dry run (Sans | sh) :

cat a.txt | xargs -I % echo "command1; command2; ... " | sh

Fonctionne aussi avec des tuyaux:

cat a.txt | xargs -I % echo "echo % | cat " | sh
5
répondu brablc 2017-09-08 07:35:59

Un peu en retard à la fête.

J'utilise le format ci-dessous pour compresser mes répertoires avec des milliers de petits fichiers avant de migrer. Si vous n'avez pas besoin de guillemets simples dans les commandes, cela devrait fonctionner.

, Avec quelques modifications, je suis sûr que ce sera utile pour quelqu'un. Testé dans Cygwin (babun)

find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }'

find . Trouver ici
-maxdepth 1 Ne pas aller dans les sous répertoires
! -path . Exclure . / Chemin du répertoire courant
-type d correspond uniquement aux répertoires
-print0 sortie séparée par octets nuls\0
| xargs tuyau à xargs
-0 L'entrée est des octets séparés par des nulles
-I @@ L'espace réservé est @@. Remplacez @@ avec entrée.
bash -c '...' exécuter la commande Bash
{...} regroupement de commandes
&& exécutez la commande suivante uniquement si la commande précédente s'est terminée avec succès (exit 0)

Final {[16] } est important, sinon il échouera.

Sortie:

Completed compressing directory ./Directory1 with meta characters in it
Completed compressing directory ./Directory2 with meta characters in it
Completed compressing directory ./Directory3 with meta characters in it

2018 Mise À Jour De Juillet:

Si vous aimez les hacks et jouer, voici quelque chose intéressant:

echo "a b c" > a.txt
echo "123" >> a.txt
echo "###this is a comment" >> a.txt
cat a.txt
myCommandWithDifferentQuotes=$(cat <<'EOF'                                     
echo "command 1: $@"; echo 'will you do the fandango?'; echo "command 2: $@"; echo
EOF
)
< a.txt xargs -I @@ bash -c "$myCommandWithDifferentQuotes" -- @@

Sortie:

command 1: a b c
will you do the fandango?
command 2: a b c

command 1: 123
will you do the fandango?
command 2: 123

command 1: ###this is a comment
will you do the fandango?
command 2: ###this is a comment

Explication:
- Créer un seul paquebot de script et de le stocker dans une variable
- xargs lit a.txt et l'exécute en tant bash script
- @@ s'assure que chaque fois qu'une ligne entière est passé
- Mettre @@ après -- permet de s'assurer de @@ est pris comme paramètre de position d'entrée de bash commande, pas un bash démarrer OPTION, comme -c lui-même ce qui signifie run command

-- est magique, il travaille avec beaucoup d'autres choses, c'est à dire ssh, même kubectl

5
répondu sdkks 2018-07-12 17:48:11

Mon BKM actuel pour cela est

... | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'

Il est regrettable que cela utilise perl, qui est moins susceptible d'être installé que bash; mais il gère plus d'entrée que la réponse acceptée. (Je salue une version omniprésente qui ne repose pas sur perl.)

La suggestion de@KeithThompson de

 ... | xargs -I % sh -c 'command1; command2; ...'

Est génial-sauf si vous avez le caractère de commentaire shell # dans votre entrée, auquel cas une partie de la première commande et toute la seconde commande seront tronquées.

Hachages # peut être assez commun, si l'entrée est dérivée d'une liste de système de fichiers, comme ls ou find, et que votre éditeur crée des fichiers temporaires avec # dans leur nom.

Exemple du problème:

$ bash 1366 $>  /bin/ls | cat
#Makefile#
#README#
Makefile
README

Oups, voici le problème:

$ bash 1367 $>  ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %'
1
1
1
1 Makefile
2 Makefile
1 README
2 README

Ahh, c'est mieux:

$ bash 1368 $>  ls | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'
1 #Makefile#
2 #Makefile#
1 #README#
2 #README#
1 Makefile
2 Makefile
1 README
2 README
$ bash 1369 $>  
1
répondu Krazy Glew 2016-01-24 16:47:26

Cela semble être la version la plus sûre.

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";'

(-0 peut être supprimé et le tr remplacé par une redirection (ou le fichier peut être remplacé par un fichier séparé null à la place). C'est principalement là depuis que je l'utilise principalement xargs avec find avec -print0 sortie) (Cela peut également être pertinent sur xargs versions sans les -0 extension)

C'est sûr, puisque args passera les paramètres au shell en tant que tableau Wehn l'exécutant. Le shell (au moins bash) les transmettrait alors comme un tableau inchangé aux autres processus lorsque tous sont obtenus en utilisant ["$@"][1]

Si vous utilisez ...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";', l'affectation échouera si la chaîne contient des guillemets doubles. Ceci est vrai pour chaque variante utilisant -i ou -I. (En raison de son remplacement dans une chaîne, vous pouvez toujours injecter des commandes en insérant des caractères inattendus (comme des guillemets, des backticks ou des signes dollar) dans les données d'entrée)

Si les commandes ne peuvent prendre qu'un seul paramètre à la fois:

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "$@"; command2 "$@";'

Ou avec un peu moins de processus:

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'for f in "$@"; do command1 "$f"; command2 "$f"; done;'

Si vous avez GNU xargs ou un autre avec l'extension -P et que vous voulez exécuter 32 processus en parallèle, chacun avec pas plus de 10 paramètres pour chaque commande:

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";'

Cela devrait être robuste contre tous les caractères spéciaux dans l'entrée. (Si l'entrée est nulle séparés.) La version tr obtiendra une entrée non valide si certaines lignes contiennent des sauts de ligne, mais cela est inévitable avec un fichier séparé par sauts de ligne.

0
répondu Gert van den Berg 2018-07-12 12:17:24