cat a.txt | xargs -I % echo %
Dans l'exemple ci-dessus, xargs prend echo %
comme argument de 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 encapsuler les commandes, mais ce n'est pas un pipeline, je ne le préfère pas. Y a-t-il une autre solution?
while
boucle qui peut contenir plusieurs commandes.Réponses:
... ou, sans utilisation inutile de chat :
Pour expliquer certains des points les plus fins:
L'utilisation de
"$arg"
au lieu de%
(et l'absence de-I
dans laxargs
ligne de commande) est pour des raisons de sécurité: la transmission de données sursh
la liste d'arguments de ligne de commande au lieu de la remplacer dans le code empêche le contenu que les données peuvent contenir (par exemple$(rm -rf ~)
, pour prendre un exemple malveillant) d'être exécuté en tant que code.De même, l'utilisation de
-d $'\n'
est une extension GNU qui fait quexargs
chaque ligne du fichier d'entrée est traitée comme une donnée distincte. Soit ceci, soit-0
(qui attend des NUL au lieu de sauts de ligne) est nécessaire pour empêcher les xargs d'essayer d'appliquer une analyse de type shell (mais pas tout à fait compatible shell) au flux qu'il lit. (Si vous n'avez pas de xargs GNU, vous pouvez utilisertr '\n' '\0' <a.txt | xargs -0 ...
pour obtenir une lecture orientée ligne sans-d
).Le
_
est un espace réservé pour$0
, de sorte que les autres valeurs de données ajoutées parxargs
deviennent$1
et à partir de, ce qui s'avère être l'ensemble de valeurs par défaut sur lequel unefor
boucle itère.la source
sh -c
- notez que le point-virgule après chaque commande n'est pas facultatif, même s'il s'agit de la dernière commande de la liste.command1
etcommand2
; J'ai réalisé plus tard qu'ils n'étaient pas nécessaires.}
:sh -c '{ command1; command2; }' -- but it's not required at the end of a command sequence that doesn't use braces:
sh -c 'command1; command2'`%
caractère quelque part dans votre chaîne passéesh -c
, cela est sujet à des failles de sécurité: un nom de fichier contenant$(rm -rf ~)'$(rm -rf ~)'
(et c'est une sous-chaîne parfaitement légale à avoir dans un nom de fichier sur des systèmes de fichiers UNIX courants!) Causera une très mauvaise journée à quelqu'un .Avec GNU Parallel, vous pouvez faire:
Regardez les vidéos d'introduction pour en savoir plus: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Pour des raisons de sécurité, il est recommandé d'utiliser votre gestionnaire de packages pour l'installation. Mais si vous ne pouvez pas le faire, vous pouvez utiliser cette installation de 10 secondes.
L'installation de 10 secondes tentera de faire une installation complète; si cela échoue, une installation personnelle; si cela échoue, une installation minimale.
la source
Ceci est juste une autre approche sans xargs ni chat:
la source
IFS
, il ignorera les espaces de début et de fin dans les noms de fichiers; sauf si vous ajoutez-r
, les noms de fichiers avec des barres obliques inverses auront ces caractères ignorés.xargs
. (Ceci est difficile à développer pour faire quelque chose de similaire àxargs
l'-P<n>
option GNU )$ command | while read line; do c1 $line; c2 $line; done
Vous pouvez utiliser
{} = variable pour chaque ligne du fichier texte
la source
file.txt
contient une donnée avec$(rm -rf ~)
comme sous-chaîne?Une chose que je fais est d'ajouter à .bashrc / .profile cette fonction:
alors vous pouvez faire des choses comme
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 de chacun, si vous avez également besoin de ce comportement.
la source
Je préfère le style qui permet le mode marche à sec (sans
| sh
):Fonctionne également avec les tuyaux:
la source
-P
option GNU xargs ... (Sinon, j'utilise principalement-exec
onfind
, car mes entrées sont principalement des noms de fichiers)Un peu tard pour 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 cela sera utile pour quelqu'un. Testé à
Cygwin
(babun)find .
Trouver ici-maxdepth 1
Ne pas aller dans les répertoires enfants! -path .
Exclure. / Le chemin du répertoire actuel-type d
correspond uniquement aux répertoires-print0
Sortie séparée par des octets nuls \ 0| xargs
Tuyau vers xargs L'-0
entrée est octets séparés par des caractères nuls L'espace-I @@
réservé est @@. Remplacez @@ par une entrée.bash -c '...'
Exécuter la commande Bash{...}
Groupement de commandes&&
Exécuter la commande suivante uniquement si la commande précédente s'est terminée avec succès (sortie 0)La finale
;
est importante, sinon elle échouera.Production:
Mise à jour de juillet 2018:
Si vous aimez les hacks et jouer, voici quelque chose d'intéressant:
Production:
Explication:
- Créez un seul script de liner et stockez-le dans une variable
- le
xargs
lita.txt
et l'exécute en tant quebash
script-
@@
s'assure à chaque fois qu'une ligne entière est passée- Le fait de mettre
@@
après--
s'assure@@
est pris comme entrée de paramètre positionnel pour labash
commande, pas unbash
débutOPTION
, c'est-à-dire comme-c
lui ce qui signifierun command
--
est magique, il fonctionne avec beaucoup d'autres choses, c'estssh
-à- dire mêmekubectl
la source
find . -type f -print0|xargs -r0 -n1 -P20 bash -c 'f="{}";ls -l "$f"; gzip -9 "$f"; ls -l "$f.gz"'
(C'est un peu plus facile lors de la conversion de boucles)"$@"
soit le seul moyen d'éviter cela ... (avec-n1
si vous voulez limiter le nombre de paramètres))--
le shell est utilisé pour dire qu'aucune autre option ne doit être acceptée. Cela permet qu'il y ait aussi un-
après--
. Vous pouvez obtenir une sortie très intéressante et déroutante si vous ne le faites pas, par exemplegrep -r
lorsque vous incluez dans le motif-
! La façon dont vous le dites ne clarifie pas cela n'explique pas vraiment comment cela fonctionne. Iirc c'est une chose POSIX mais de toute façon cela vaut la peine de le souligner, je pense. Juste quelque chose à considérer. Et j'adore ce bonus btw!Cela semble être la version la plus sûre.
(
-0
peut être supprimé ettr
remplacé par une redirection (ou le fichier peut être remplacé par un fichier séparé par null à la place). Il est principalement là car j'utilise principalementxargs
avecfind
avec-print0
sortie) (Cela peut également être pertinent sur lesxargs
versions sans l'-0
extension)C'est sûr, car args transmettra les paramètres au shell sous forme de tableau lors de son exécution. Le shell (au moins
bash
) les transmettrait ensuite sous la forme d'un tableau non modifié aux autres processus lorsque tous sont 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. Cela 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 guillemets ou des signes dollar) dans les données d'entrée)Si les commandes ne peuvent prendre qu'un paramètre à la fois:
Ou avec un peu moins de processus:
Si vous avez GNU
xargs
ou un autre avec l'-P
extension et que vous souhaitez exécuter 32 processus en parallèle, chacun avec pas plus de 10 paramètres pour chaque commande:Cela devrait être robuste contre tous les caractères spéciaux dans l'entrée. (Si l'entrée est séparée par des valeurs nulles.) La
tr
version obtiendra une entrée non valide si certaines des lignes contiennent des retours à la ligne, mais cela est inévitable avec un fichier séparé par des retours à la ligne.Le premier paramètre vide pour
bash -c
est dû à ceci: (À partir de labash
page de manuel ) (Merci @clacke)la source
"$@"
bash -c 'command1 "$@"; command2 "$@";' arbitrarytextgoeshere
bash
avec-c
prend d'abord (après les commandes) un argument qui sera le nom du processus, puis il prend les arguments positionnels. Essayez debash -c 'echo "$@" ' 1 2 3 4
voir ce qui sort.Une autre solution possible qui fonctionne pour moi est quelque chose comme -
Notez le 'bash' à la fin - je suppose qu'il est passé en argv [0] à bash. Sans lui dans cette syntaxe, le premier paramètre de chaque commande est perdu. Ce peut être n'importe quel mot.
Exemple:
la source
"$@"
, vous divisez les chaînes et développez la liste d'arguments de manière globale.Mon BKM actuel pour cela est
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.)
@ Suggestion de KeithThompson
est génial - à moins que vous n'ayez le caractère de commentaire du shell # dans votre entrée, auquel cas une partie de la première commande et toute la deuxième commande seront tronquées.
Les hachages # peuvent être assez courants, si l'entrée est dérivée d'une liste de systèmes de fichiers, telle que ls ou find, et que votre éditeur crée des fichiers temporaires avec # dans leur nom.
Exemple du problème:
Oups, voici le problème:
Ahh, c'est mieux:
la source
ls | xargs -I % sh -c 'echo 1 "%"; echo 2 "%"'