2016-06-08 6 views
0

J'ai écrit un programme qui attend des données sur une entrée standard, l'écrit dans un emplacement temporaire et le déplace vers un emplacement spécifié par l'utilisateur à la fin de l'entrée.Dans un canal, comment un programme en aval peut-il obtenir le code de sortie d'un programme en amont après la fin de l'entrée?

Par cela, je veux permettre aux pipelines comme celui-ci

# Does not work; truncates myfile 
cat myfile | some_filter > myfile 
# shall enable pseudo-inplace modification 
cat myfile | some_filter | my_program myfile 

Mais mon programme écrit actuellement le dossier indépendamment du fait que le tuyau a réussi en amont. Je veux éviter la perte de données si en amont rencontré une erreur et donc abandonner le programme si l'un des programmes en amont du tuyau a jeté une erreur. Pour l'instant, j'ai écrit le programme dans bash mais je ne suis pas limité à cela.

Comment puis-je vérifier cela dans mon programme? Pour être plus précis: je veux épargner à mon utilisateur la création de fichiers temporaires, vérifier autant que possible le succès des programmes intermédiaires et similaires. Je veux que l'utilisateur puisse appeler un programme (par exemple ajouter une colonne à un fichier texte, trier, filtrer, n'importe quoi) sur un fichier et écrire le résultat dans ce même fichier, mais seulement si les programmes intermédiaires ont réussi.

+1

Vous pouvez utiliser mktemp pour créer un fichier temporaire, écrire dans le fichier temporaire, vérifier si le processus s'est terminé avec succès, puis écrire à partir du fichier temporaire dans votre fichier, supprimer le fichier temporaire. – dood

+2

Pour expliquer ce que @dood a dit: 'tmpfile = $ (mktemp); processus1 | process2> $ tmpfile && mv $ tmpfile monfichier – anishsane

+1

Aussi, 'cat myfile | some_filter' est [UUOC] (http://porkmail.org/era/unix/award.html#cat). Utilisez 'some_filter anishsane

Répondre

1

Une redirection, par exemple > file est gérée par le shell qui ouvre le fichier en écriture avant d'appeler la commande. Une solution pour cela est - comme suggéré par @dood dans les commentaires, en utilisant un fichier temporaire:

tmp=$(mktemp) 
a file | b | c > "$tmp"; && mv "$tmp" file 

Maintenant, le fichier créé par mktemp est utilisé pour la redirection. Et puis après le fichier est déplacé à file.

Cela ne fera que vérifier la dernière commande dans le tube et se déplacer si cela a réussi.

En il y a une variable nommée PIPESTATUS qui contient tous les états de sortie pour le tuyau, par exemple:

% ls | cat | cat 
% echo "${PIPESTATUS[@]}" 
0 0 0 

Et un échec:

% ls | cat | false 
% echo "${PIPESTATUS[@]}" 
0 141 1 

Vous pouvez l'utiliser pour vérifier si toutes les commandes dans le tuyau succès sorti:

% ls | cat | cat 
% [[ "${PIPESTATUS[@]}" =~ ^0(0)*$ ]] && echo "good" 
good 

Et wi th votre exemple:

tmp=$(mktemp) 
some_filter <myfile> "$tmp" 
[[ "${PIPESTATUS[@]}" =~ ^0(0)*$ ]] && mv "$tmp" "myfile" 

Le regex ^0(0)*$ correspond 0, 0 0, 0 0 0 ... Alors correspond en gros si toutes les commandes de la conduite sont parvenus à sortir.

+0

C'est exactement ce que je veux encapsuler dans un programme: mon utilisateur n'a pas à s'embêter à créer un fichier temporaire, à lire l'état du tube et à s'assurer que le fichier temporaire est supprimé par la suite. – akraf

1

Le point d'exécution des commandes dans un pipeline est qu'elles s'exécutent simultanément. Lorsque vous exécutez cmd1 | cmd2 | cmd3, cmd3 commence très probablement à s'exécuter avant la fin de cmd1. Par conséquent, à moins de pouvoir invoquer une anomalie temporelle de style Star Trek, il est impossible de déterminer le statut de sortie de cmd1 avant le démarrage de cmd3. Si vous devez attendre que cmd1 se termine avant de commencer cmd3, vous ne pouvez pas les placer dans le même pipeline.Eh bien, ce n'est pas tout à fait vrai, vous pouvez faire des hacks comme;

mkfifo /tmp/foo; { cmd1; echo > /tmp/foo; } | cmd2 | { cat /tmp/foo; cmd3; }

mais pourquoi voudriez-vous? (Notez que même dans ce cas, les commandes dans la dernière partie de la canalisation ne s'exécuter avant se termine cmd1, mais les cat blocs afin que l'exécution de cmd3 est retardée.)

Notez également que peu importe ce que vous essayez, il y a une impasse potentielle. Si les tuyaux sont pleins, cmd1 et cmd2 se bloqueront indéfiniment sur une écriture et cmd3 ne commencera jamais.

+0

Merci pour cette explication. Mais est-il possible d'avoir le cours suivant: cmd1, cmd2 et cmd3 commencent tous, cmd3 lit toute la sortie de cmd1 | cmd2, jusqu'à la fin de la transmission. Cela signifie que cmd2 a fermé sa sortie, donc cmd3 devrait être en mesure d'obtenir le statut de sortie de cmd2 à ce moment-là? – akraf

+0

Bien sûr: 'cmd1 | cmd2>/tmp/foo && cmd3