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?
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.
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
C'est juste une autre approche sans xargs ni chat:
while read stuff; do
command1 "$stuff"
command2 "$stuff"
...
done < a.txt
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.
Vous pouvez utiliser
cat file.txt | xargs -i sh -c 'command {} | command2 {} && command3 {}'
{} = variable pour chaque ligne du fichier texte
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
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
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
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 $>
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.