Disons que j'ai un tableau:
cat template > output for var in "${ITEMS[@]}"; do cat output | my_template $var > output done
Je veux exécuter la commande suivante:
cat template \ | my_command $ITEMS[1] \ | my_command $ITEMS[2] \ | my_command $ITEMS[3] \ > output
Je ne veux pas trop coder le ma_commande
pour chaque élément du tableau, car le tableau est dynamique.
Je peux faire ce qui suit:
ITEMS=( "foo" "bar" "baz" )
Mais cela semble maladroit car il écrit plusieurs fois dans le fichier.
Existe-t-il un moyen d'obtenir toutes les invocations de ma_commande
dans la même commande, quand il y a un nombre inconnu d'invocations?
3 Réponses :
Voici un moyen d'utiliser printf
et le très décrié eval
:
eval $(printf 'cat template '; printf '| my_command "%s" ' "${ITEMS[@]}") > output
Utilisez-le uniquement lorsque vous êtes sûr qu'il y a aucune entrée malveillante dans le tableau ITEMS
.
Vous pouvez rediriger tout le bloc de boucle vers la sortie, donc:
my_template "foo" "bar" "baz" <template
Alternativement, si votre programme my_template
peut accepter plusieurs éléments comme arguments, vous pouvez le transmettre tout le tableau en un seul appel avec:
my_template "${ITEMS[@]}" <template
Cela appellera:
for var in "${ITEMS[@]}"; do my_template "${var}" <template done >output
J'aime la première approche, et je pense que cela fonctionnera dans mon cas, mais est-ce que cela fonctionnerait si (hypothétiquement) chaque appel de my_command
dépendait de la sortie précédente?
Non, chaque appel traite le template
indépendamment des autres.
@FelaMaslen vous pourriez probablement enchaîner l'entrée et la sortie dans la boucle en utilisant un tube nommé: mypipe = $ (mktemp -u); mkfifo -m 600 "$ {mypipe}"; pour var dans "$ {ITEMS [@]}"; do my_template "$ {var}" <"$ {mypipe}" >> "$ {mypipe}"; Fini; cat "$ {mypipe}"> sortie; rm "$ {mypipe}"
Utilisez une bonne récursion à l'ancienne.
1)
), exécutez la commande directement. *)
), exécutez-le avec le premier argument ( $ {args [0]}
) et dirigez-le vers un appel récursif de multipipe
avec le premier argument supprimé ( $ {args [@]: 1}
). multipipe() { local cmd=$1 local args=("${@:2}") case ${#args[@]} in 0) ;; 1) "$cmd" "${args[0]}";; *) "$cmd" "${args[0]}" | multipipe "$cmd" "${args[@]:1}";; esac } multipipe my_command "${ITEMS[@]}" <template >output
C'est la solution la plus claire et ne dépend pas de eval
. +1
my_command
n'accepte-t-il qu'un seul paramètre d'entrée? Ou pourriez-vous réécriremy_command
pour accepter plusieurs valeurs?ma_commande
est un programme externe que je n'ai pas écrit, alors disons qu'il doit être au format fourni.