Existe-t-il un moyen de lire les lignes de la sortie de commande?

8

J'ai une commande de pré-processus pour sortir un fichier

./preprocess.sh > preprocessed_file 

et le preprocessed_filesera utilisé comme ça

while read line
do

    ./research.sh $line &

done < preprocessed_file 

rm -f preprocessed_file

Existe-t-il un moyen de diriger la sortie vers la while read linepièce au lieu de la sortir vers le fichier préprocédé? Je pense qu'il devrait y avoir un meilleur moyen que d'utiliser ce temp preprocessed_file.

Marcus Thornton
la source

Réponses:

8

Vous pouvez utiliser la substitution de processus bash :

while IFS= read -r line; do
  ./research.sh "$line" &
done < <(./preprocess.sh)

Quelques avantages de la substitution de processus:

  • Pas besoin de sauvegarder des fichiers temporaires.
  • Meilleure performance. Lecture à partir d'un autre processus souvent plus rapide que l'écriture sur disque, puis relecture.
  • Gagnez du temps dans le calcul car lorsqu'il est effectué simultanément avec l'expansion des paramètres et des variables, la substitution de commandes et l'expansion arithmétique
cuonglm
la source
que signifie la double flèche gauche (<<)?
Marcus Thornton
@MarcusThornton: <est une redirection, tandis que la <(...)syntaxe de substitution de processus. Vous devriez lire: gnu.org/software/bash/manual/html_node/… pour plus de détails.
cuonglm
Je l'ai. <(...)fait partie de la syntaxe.
Marcus Thornton
2
Ce n'est pas nécessairement plus rapide. Parce que lors de la lecture à partir d'un canal readdoit lire un octet à la fois, alors qu'il peut optimiser les choses avec la lecture de gros morceaux et rechercher en arrière lors de la lecture à partir d'un fichier normal. Le mieux est d'éviter les while readboucles en premier lieu lorsque cela est possible. Notez également que vous devez IFS= read -r linelire la ligne $line. Et laisser sans $lineguillemets (invoquer l'opérateur split + glob) ici n'a probablement aucun sens.
Stéphane Chazelas
1
@mikeserv, les commandes mettent souvent en mémoire tampon de ligne (par opposition à la mémoire tampon complète) leur sortie lorsqu'elle va à un terminal. Ici, je dis que le readshell intégré lit un caractère à la fois lors de la lecture à partir d'un tuyau (indépendamment de ce qui est à l'autre extrémité du tuyau qui readn'a aucun moyen de savoir), ce qui est l'une des raisons pour lesquelles les while readboucles sont extrêmement lentes.
Stéphane Chazelas
15

Oui! Vous pouvez utiliser un tuyau de processus |.

./preprocess.sh |
    while IFS= read -r line
    do
        ./research.sh "$line" &
    done

Un tube de processus transmet la sortie standard ( stdout) d'un processus à l'entrée standard ( stdin) du suivant.

Vous pouvez éventuellement mettre un caractère de nouvelle ligne après un |et étendre la commande à la ligne suivante.

Remarque: a|best équivalent à b < <(a), mais sans les fichiers magiques, et dans un ordre plus lisible, en particulier lorsque le pipeline s'allonge.

a|b|c est équivalent à c < <(b < <(a))

et

a|b|c|d|e est e < < (d < <(c < <(b < <(a))))

ctrl-alt-delor
la source
3
Remarque: Cette solution avec le tube a l'avantage d'être plus portable que la substitution de processus (non prise en charge par certains shells POSIX comme dash). Toujours concernant la portabilité, le côté droit d'un tube peut être exécuté dans un sous-shell (cela dépend du shell), de sorte que tout effet secondaire (comme la définition de variables) ne puisse pas affecter l'environnement du script shell.
vinc17
Il est généralement plus sûr de mettre des références de variables comme $lineentre guillemets doubles (par exemple, dans votre script, ./research.sh "$line" &).
G-Man dit `` Réintègre Monica ''
1
@ G-Man Peut-être pas dans ce contexte. Si research.shfonctionne avec le tableau d'arguments de ligne de commande et $lineest, par exemple, "un deux", avec l'intention que le premier argument soit "un" et le deuxième argument "deux", la citation $linerendra cela impossible - à la place le premier argument sera "un deux" et il n'y en aura pas un deuxième ...
goldilocks
2
" a|béquivaut àb < <(a) " - proche, mais pas tout à fait. Dans la version de canal, les deux côtés du canal sont exécutés en sous-shell, tandis que dans la version de substitution de processus, seul le processus substitué est exécuté dans un sous-shell, mais aest exécuté dans le cadre du niveau de shell en cours d'exécution. Cela a des implications importantes pour la portée des variables définies dansa
Digital Trauma