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 pour traiter l'argument 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. Y a-t-il une autre solution?
cat a.txt | xargs -I % sh -c 'command1; command2; ...'
Notez que ceci est un Utilisation inutile de chat . 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
contiendront un ou plusieurs caractères %
; sinon, il n'y aurait pas grand intérêt à utiliser l'option -I %
pour xargs
.
Avec GNU parallèle, 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
Ceci est juste une autre approche sans xargs ni cat:
while read stuff; do
command1 "$stuff"
command2 "$stuff"
...
done < a.txt
Vous pouvez utiliser
cat file.txt | xargs -i sh -c 'command {} | command2 {} && command3 {}'
{} = variable pour chaque ligne du fichier texte
Une chose que je fais est d'ajouter à .bashrc/.profile cette fonction:
function each() {
while read line; do
for f in "$@"; do
$f $line
done
done
}
alors 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, si vous avez également besoin de ce comportement.
Je préfère le style qui permet le mode de fonctionnement à sec (sans | sh
):
cat a.txt | xargs -I % echo "command1; command2; ... " | sh
Fonctionne aussi avec des pipes:
cat a.txt | xargs -I % echo "echo % | cat " | sh
Un peu tard pour la fête.
J'utilise le format ci-dessous pour compresser mes répertoires avec des milliers de fichiers minuscules 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 .
Trouvez ici-maxdepth 1
Ne pas aller dans les répertoires enfants! -path .
Exclude./Chemin du répertoire actuel-type d
ne correspondent qu'aux répertoires-print0
Sortie séparée par des octets nuls\0| xargs
Pipe to xargs-0
L'entrée est composée d'octets séparés par NULL-I @@
Placeholder est @@. Remplacez @@ par une entrée.bash -c '...'
Commande Run Bash{...}
Groupement de commandes&&
N'exécute la commande suivante que si la commande précédente a été sortie avec succès (exit 0)
Le ;
final 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
Mise à jour de juillet 2018:
Si vous aimez les hacks et les jeux, voici quelque chose d'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 script simple et le stocker dans une variable
- xargs
lit a.txt
et l'exécute en tant que script bash
- @@
s'assure chaque fois qu'une ligne entière est passée
- Mettre @@
après --
permet de s’assurer que @@
est pris comme entrée de paramètre de position dans la commande bash
et non comme une variable bash
start OPTION
, c’est-à-dire comme -c
lui-même qui signifie run command
--
est magique, il fonctionne avec beaucoup d’autres choses, par exemple ssh
, même kubectl
Une autre solution possible 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 cette syntaxe, le premier paramètre de chaque commande est perdu. Ce 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
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 me félicite d'une version omniprésente qui ne repose pas sur Perl.)
La suggestion de @ KeithThompson de
... | xargs -I % sh -c 'command1; command2; ...'
is great - sauf si vous avez le caractère de commentaire Shell dans votre entrée, auquel cas une partie de la première commande et toutes les secondes seront tronquées.
Les hachages # peuvent être assez courants, si l'entrée est dérivée d'une liste de système de fichiers, telle que ls ou find, et que votre éditeur crée des fichiers temporaires avec le nom #.
Exemple du problème:
$ bash 1366 $> /bin/ls | cat
#Makefile#
#README#
Makefile
README
Oops, 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 la tr
remplacée par une redirection (ou le fichier peut être remplacé par un fichier séparé à la place). Il est principalement présent car j’utilise principalement xargs
avec find
with -print0
output) (Cela pourrait également être pertinent sur les versions xargs
sans l'extension -0
)
C’est sûr, car args transmettra les paramètres au shell en tant que tableau pour l’exécuter. Le shell (au moins bash
) les transmettrait ensuite aux autres processus sous forme de tableau non modifié lorsque tous seraient obtenus à l'aide de ["$@"][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 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 si vous souhaitez exécuter 32 processus en parallèle, chacun ne contenant 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 séparée de zéro.) La version tr
obtiendra une entrée non valide si certaines lignes contiennent des nouvelles lignes, mais cela est inévitable avec un fichier séparé par une nouvelle ligne.