86 votes

Capturer stdout dans une variable tout en l'affichant dans la console

J'ai un script bash script qui appelle plusieurs processus de longue durée. Je veux capturer la sortie de ces appels dans des variables pour des raisons de traitement. Cependant, comme il s'agit de processus de longue durée, j'aimerais que la sortie des appels rsync soit affichée dans la console en format en temps réel et non après coup.

À cette fin, j'ai trouvé une façon de le faire, mais elle repose sur la sortie du texte sur /dev/stderr. J'ai le sentiment que la sortie vers /dev/stderr n'est pas une bonne façon de faire.

VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee /dev/stderr)

VAR2=$(rsync -r -t --out-format='%n%L' --delete -s /path/source1/ /path/target1 | tee /dev/stderr)

VAR3=$(rsync -r -t --out-format='%n%L' --delete -s /path/source2/ /path/target2 | tee /dev/stderr)

Dans l'exemple ci-dessus, j'appelle rsync plusieurs fois et je veux voir les noms des fichiers au fur et à mesure qu'ils sont traités, mais à la fin, je veux toujours que la sortie soit dans une variable parce que je l'analyserai plus tard.

Existe-t-il un moyen plus "propre" d'y parvenir ?

Si cela fait une différence, j'utilise Ubuntu 12.04, bash 4.2.24.

91voto

Op De Cirkel Points 8632

Dupliquez &1 dans votre shell (dans mon exemple à 5) et utilisez &5 dans le subshell (afin d'écrire sur stdout (&1) du shell parent) :

exec 5>&1
FF=$(echo aaa|tee >(cat - >&5))
echo $FF

Imprime aaa deux fois, une fois à cause de l'écho dans la sous-shell, et la deuxième fois imprime la valeur de la variable.

Dans votre code :

exec 5>&1
VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee >(cat - >&5))
# use the value of VAR1

43voto

Russell Davis Points 2949

La réponse d'Op De Cirkel va dans le bon sens. Elle peut être simplifiée encore davantage (en évitant l'utilisation de cat ) :

exec 5>&1
FF=$(echo aaa|tee /dev/fd/5)
echo $FF

19voto

Bryan Roach Points 28

Voici un exemple de capture des deux stderr et le code de sortie de la commande. Ceci est basé sur la réponse de Russell Davis.

exec 5>&1
FF=$(ls /taco/ 2>&1 |tee /dev/fd/5; exit ${PIPESTATUS[0]})
exit_code=$?
echo "$FF"
echo "Exit Code: $exit_code"

Si le dossier /taco/ existe, cela permettra de capturer son contenu. Si le dossier n'existe pas, un message d'erreur sera affiché et le code de sortie sera 2.

Si vous omettez 2>&1 alors seulement stdout sera capturé.

8voto

tripleee Points 28746

Si par "la console" vous entendez votre ATS actuel, essayez

variable=$(command with options | tee /dev/tty)

Il s'agit d'une pratique légèrement douteuse car les personnes qui essaient de l'utiliser sont parfois surprises de voir la sortie atterrir à un endroit inattendu lorsqu'elles n'ont pas d'ATS (tâches cron, etc.).

7voto

Piotr Wadas Points 1412

Vous pouvez utiliser plus de trois descripteurs de fichiers. Essayez ici :

http://tldp.org/LDP/abs/html/io-redirection.html

"Chaque fichier ouvert se voit attribuer un descripteur de fichier. [Les descripteurs de fichier pour stdin, stdout et stderr sont respectivement 0, 1 et 2. Pour ouvrir d'autres fichiers, il reste les descripteurs 3 à 9. Il est parfois utile d'attribuer l'un de ces descripteurs de fichier supplémentaires à stdin, stdout ou stderr en tant que lien dupliqué temporaire".

La question est de savoir s'il vaut la peine de rendre script plus compliqué juste pour obtenir ce résultat. En fait, ce n'est pas vraiment une erreur, de la manière dont vous le faites.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X