web-dev-qa-db-fra.com

xargs avec plusieurs arguments

J'ai une entrée source, input.txt

a.txt
b.txt
c.txt

Je veux alimenter ces entrées dans un programme comme suit:

my-program --file=a.txt --file=b.txt --file=c.txt

J'essaie donc d'utiliser xargs , mais sans succès.

cat input.txt | xargs -i echo "my-program --file"{}

Il donne

my-program --file=a.txt
my-program --file=b.txt
my-program --file=c.txt

Mais je veux 

my-program --file=a.txt --file=b.txt --file=c.txt

Une idée?

43
Howard

Aucune des solutions proposées jusqu'ici ne traite correctement les noms de fichiers contenant de l'espace. Certains échouent même si les noms de fichiers contiennent 'ou ". Si vos fichiers d'entrée sont générés par les utilisateurs, vous devriez être préparé à des noms de fichiers surprenants.

GNU Parallel traite bien ces noms de fichiers et vous donne (au moins) 3 solutions différentes. Si votre programme prend 3 et seulement 3 arguments, cela fonctionnera:

(echo a1.txt; echo b1.txt; echo c1.txt;
 echo a2.txt; echo b2.txt; echo c2.txt;) |
parallel -N 3 my-program --file={1} --file={2} --file={3}

Ou:

(echo a1.txt; echo b1.txt; echo c1.txt;
 echo a2.txt; echo b2.txt; echo c2.txt;) |
parallel -X -N 3 my-program --file={}

Si, toutefois, votre programme prend autant d'arguments que la ligne de commande en tiendra compte:

(echo a1.txt; echo b1.txt; echo c1.txt;
 echo d1.txt; echo e1.txt; echo f1.txt;) |
parallel -X my-program --file={}

Regardez la vidéo d'introduction pour en savoir plus: http://www.youtube.com/watch?v=OpaiGYxkSuQ

22
Ole Tange

ne les écoutez pas tous :) regardez juste cet exemple:

echo argument1 argument2 argument3 | xargs -l bash -c 'echo this is first:$0 second:$1 third:$2' | xargs

la sortie sera

this is first:argument1 second:argument2 third:argument3
63
sauliux

Que diriez-vous:

echo $'a.txt\nb.txt\nc.txt' | xargs -n 3 sh -c '
   echo my-program --file="$1" --file="$2" --file="$3"
' argv0
16
yabt

Vous pouvez utiliser sed pour préfixer --file= à chaque ligne, puis appeler xargs:

sed -e 's/^/--file=/' input.txt | xargs my-program
5
Bart Sas

Voici une solution utilisant sed pour trois arguments, mais limitée en ce sens qu'elle applique la même transformation à chaque argument:

cat input.txt | sed 's/^/--file=/g' | xargs -n3 my-program

Voici une méthode qui fonctionnera pour deux arguments, mais permet plus de flexibilité:

cat input.txt | xargs -n 2 | xargs -I{} sh -c 'V="{}"; my-program -file=${V% *} -file=${V#* }'
5
maharvey67

Je suis tombé sur un problème similaire et ai trouvé une solution qui, à mon avis, est meilleure et plus propre que celles présentées jusqu'à présent.

La syntaxe pour xargs que j'ai terminée serait (par exemple):

xargs -I X echo --file=X

avec une ligne de commande complète étant:

my-program $(cat input.txt | xargs -I X echo --file=X)

qui fonctionnera comme si

my-program --file=a.txt --file=b.txt --file=c.txt

a été fait (à condition que input.txt contienne les données de votre exemple).


En fait, dans mon cas, je devais d'abord trouver les fichiers, puis les trier afin que ma ligne de commande ressemble à ceci:

my-program $(find base/path -name "some*pattern" -print0 | sort -z | xargs -0 -I X echo --files=X)

Quelques détails qui pourraient ne pas être clairs (ils n'étaient pas pour moi):

  • some*pattern doit être cité, sinon Shell le développerait avant de passer à find.
  • -print0, puis -z et enfin -0 utilisent une séparation nulle pour garantir le traitement correct des fichiers avec des espaces ou d'autres noms câblés.

Notez cependant que je ne l'ai pas encore testé profondément. Bien que cela semble fonctionner.

2
Adam Badura

Je cherchais une solution à ce problème précis et suis parvenu à la conclusion de coder un script dans le midle.

pour transformer la sortie standard de l'exemple suivant, utilisez le séparateur -n '\ n'

exemple:

 user@mybox:~$ echo "file1.txt file2.txt" | xargs -n1 ScriptInTheMiddle.sh

 inside the ScriptInTheMidle.sh:
 !#/bin/bash
 var1=`echo $1 | cut -d ' ' -f1 `
 var2=`echo $1 | cut -d ' ' -f2 `
 myprogram  "--file1="$var1 "--file2="$var2 

Pour que cette solution fonctionne, vous devez laisser un espace entre les arguments file1.txt et file2.txt, ou le délimiteur que vous choisissez, encore une fois, dans le script, assurez-vous de cocher -f1 et -f2 car ils signifient "prendre les premier mot et prenez le deuxième mot "en fonction de la position du premier séparateur trouvée (les délimètres peuvent être" ";" "." comme vous le souhaitez entre guillemets simples. Ajoutez autant de paramètres que vous le souhaitez.

Problème résolu en utilisant xargs, cut et quelques scripts bash.

À votre santé!

si vous voulez passer, j'ai quelques conseils utiles http://hongouru.blogspot.com

1
HoNgOuRu

En fait, c'est relativement facile:

... | sed 's/^/--prefix=/g' | xargs echo | xargs -I PARAMS your_cmd PARAMS

Le sed 's/^/--prefix=/g' est facultatif, au cas où vous auriez besoin de préfixer chaque paramètre avec un --prefix =.

Le xargs echo transforme la liste des lignes de paramètres (un paramètre par ligne) en une liste de paramètres sur une seule ligne et le xargs -I PARAMS your_cmd PARAMS vous permet d'exécuter une commande en plaçant les paramètres à l'endroit souhaité.

Donc, cat input.txt | sed 's/^/--file=/g' | xargs echo | xargs -I PARAMS my-program PARAMS fait ce dont vous avez besoin (en supposant que toutes les lignes du fichier input.txt sont simples et peuvent être considérées comme une valeur param unique).

1
Moshe Bixenshpaner

C'est parce que echo imprime une nouvelle ligne. Essayez quelque chose comme

echo my-program `xargs --arg-file input.txt -i echo -n " --file "{}`
1
aioobe

xargs ne fonctionne pas de cette façon. Essayer:

 monprogramme $ (sed -e 's/^/- file = /' input.txt) 
1
Burton Samograd

C'est plus simple si vous utilisez deux invocations xargs: 1ère pour transformer chaque ligne en --file=..., 2ième pour faire la chose xargs ->

$ cat input.txt | xargs -I@ echo --file=@ | xargs echo my-program
my-program --file=a.txt --file=b.txt --file=c.txt
1
jjo

Personne n'a encore mentionné l'écho d'une boucle. Je vais donc en parler par souci de complétude (ce serait ma deuxième approche, le sed étant la première):

for line in $(< input.txt) ; do echo --file=$line ; done | xargs echo my-program
0
conny