2010-12-20 5 views
18

(Peut-être liée à Do some programs not accept process substitution for input files?)substitution de processus Bash et la synchronisation

Dans certains scripts de tests unitaires Bash J'utilise l'astuce suivante pour vous connecter et stdout d'affichage et stderr d'une commande:

command > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2) 

Ce processus produit une sortie vers stdout, donc le fichier $stdoutF obtient des données. Ensuite, je lance une autre commande qui ne transmet pas les données:

diff -r "$source" "$target" > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2) 

Cependant, il ne semble pas que ce processus se termine toujours avec succès avant le test de vide est exécuté (en utilisant shunit-ng):

assertNull 'Unexpected output to stdout' "$(<"$stdoutF")" 

Lors d'un test de 100 tests, celui-ci a échoué 25 fois.

Faut-il suffisant pour appeler sync avant de tester le fichier pour le vide:

sync 
assertNull 'Unexpected output to stdout' "$(<"$stdoutF")" 

... et/ou devrait-il fonctionner en forçant la séquence des commandes:

diff -r "$source" "$target" \ 
> >(tee "${stdoutF}"; assertNull 'Unexpected output to stdout' "$(<"$stdoutF")") 
2> >(tee "${stderrF}" >&2) 

. .. et/ou est-il possible tee en quelque sorte à assertNull directement à la place d'un fichier?

Mise à jour: sync n'est pas la réponse - Voir la réponse de Gilles ci-dessous.

Mise à jour 2: Discussion effectuée en outre à Save stdout, stderr and stdout+stderr synchronously. Merci pour les réponses!

Répondre

27

En bash, une commande de substitution de substitution de processus foo > >(bar) finitions dès foo finitions. (Ce n'est pas abordée dans la documentation.) Vous pouvez vérifier avec

: > >(sleep 1; echo a) 

Cette commande retourne immédiatement, puis imprime a de manière asynchrone une seconde plus tard.

Dans votre cas, la commande tee prend juste un peu de temps à se terminer après command. Ajoutant sync a donné tee assez de temps pour terminer, mais cela ne supprime pas la condition de la course, pas plus que l'ajout d'un sleep, il rend juste la course plus improbable de se manifester.

Plus généralement, sync n'a aucun effet observable en interne: cela ne fait que faire la différence si vous voulez accéder au périphérique où vos systèmes de fichiers sont stockés sous une instance de système d'exploitation différente. En termes plus clairs, si votre système perd de la puissance, seules les données écrites avant la dernière sync seront disponibles après le redémarrage.

En ce qui concerne la suppression de la condition de course, voici quelques-unes des approches possibles:

  • synchronisez tous les processus Explicitement substitués.

    mkfifo sync.pipe 
    command > >(tee -- "$stdoutF"; echo >sync.pipe) 
         2> >(tee -- "$stderrF"; echo >sync.pipe) 
    read line < sync.pipe; read line < sync.pipe 
    
  • Utilisez un nom de fichier temporaire différent pour chaque commande au lieu de réutiliser $stdoutF et $stderrF, et que le fichier appliquer temporaire est toujours nouvellement créé.

  • Abandonnez la substitution de processus et utilisez plutôt des tuyaux.

    { { command | tee -- "$stdoutF" 1>&3; } 2>&1 \ 
          | tee -- "$stderrF" 1>&2; } 3>&1 
    

    Si vous avez besoin de l'état de retour de la commande, il met en bash ${PIPESTATUS[0]}.

    { { command | tee -- "$stdoutF" 1>&3; exit ${PIPESTATUS[0]}; } 2>&1 \ 
          | tee -- "$stderrF" 1>&2; } 3>&1 
    if [ ${PIPESTATUS[0]} -ne 0 ]; then echo command failed; fi 
    
+0

Merci pour cette réponse impressionnante, ce vient de me faire une meilleure administration! –

-1

Insérer un sleep 5 ou tout le reste à la place de sync pour répondre à votre dernière question

1

parfois je mis un garde:

: > >(sleep 1; echo a; touch guard) \ 
    && while true; do 
    [ -f "guard" ] && { rm guard; break; } 
    sleep 0.2 
    done  
Questions connexes