1
votes

Run pipe pour chaque élément du tableau bash

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?


2 commentaires

my_command n'accepte-t-il qu'un seul paramètre d'entrée? Ou pourriez-vous réécrire my_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.


3 Réponses :


0
votes

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 .


0 commentaires

0
votes

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


3 commentaires

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}"



3
votes

Utilisez une bonne récursion à l'ancienne.

  • S'il n'y a qu'un seul argument (le cas 1) ), exécutez la commande directement.
  • S'il y en a plusieurs (le cas *) ), 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


1 commentaires

C'est la solution la plus claire et ne dépend pas de eval . +1