2010-06-29 4 views
3

Vous pouvez diviser un tuyau à l'aide de la commande tee sous linux comme suitEst-ce que l'ordre de départ de ce tirage sur stdout est garanti?

printf "line1\nline2\nline3\n" | tee >(wc -l) | (awk '{print "this is awk: "$0}')

qui donne la sortie

this is awk: line1 
this is awk: line2 
this is awk: line3 
this is awk: 3 

Ma question, est que l'ordre d'impression garantie? Le tube divisé qui compte le nombre de lignes sera-t-il toujours imprimé à la fin? Est-il possible de toujours l'imprimer au début? Ou l'ordre d'impression tee n'est-il jamais garanti?

Répondre

2

Ce n'est pas défini par tee, mais comme dit Daenyth, nous ne serons pas finis tant que le tee n'aura pas fini de transmettre les données - donc généralement tee l'aura passé à awk. Dans ce cas, il pourrait être préférable d'avoir awk faire le comptage. L'inconvénient étant qu'il ne connaîtra pas le nombre jusqu'à ce qu'il se termine (sachant qu'il nécessite la mise en mémoire tampon des données). Dans votre exemple, tee et wc ont tous les deux stdout connectés au même canal (stdin for awk), mais l'ordre n'est pas défini. cat (et la plupart des autres outils de tuyauterie) peuvent être utilisés pour assembler des fichiers dans un ordre connu.

Il existe des techniques de tuyauterie plus avancées qui pourraient être utilisées, telles que les coprocords bash (coproc) ou les tuyaux nommés (mkfifo ou mknod p). Ce dernier vous obtient des noms dans le système de fichiers, qui peuvent être transmis à d'autres processus, mais vous devrez les nettoyer et éviter les collisions. Le fichier tempfile ou $$ peut être utile pour cela. Les tuyaux ne servent pas à stocker des données, car ils ont souvent une taille limitée et bloquent simplement les écritures.

Un exemple où les tuyaux sont la mauvaise solution:

mkfifo wcin wcout 
wc -l <wcin> wcout & 
yes | dd count=1 bs=8M | tee wcin | cat -n wcout - | head 

Le problème est ici que tee va se retrouver coincé en essayant d'écrire des choses à chat, qui veut en finir avec wcout premier. Il y a simplement trop de données pour le tuyau du tee au chat.

Édition concernant la réponse de dmckee: Oui, la commande peut être répétable, mais elle n'est pas garantie. C'est une question d'échelle, d'ordonnancement et de taille de tampon. Dans cette boîte GNU/Linux, l'exemple commence à casser après quelques milliers de lignes:

seq -f line%g 20000 | tee >(awk '{print "*" $0 "*"}') | \ 
(awk '{print "this is awk: "$0}') | less 
this is awk: line2397 
this is awk: line2398 
this is awk: line2*line1* 
this is awk: *line2* 
this is awk: *line3* 
+0

lol Je vous demande un tutoriel sur faire des pipes nommés faire vous semblez connaître vos trucs, mais qui peut être trop demander beaucoup :) – ldog

+0

L'exemple cassé ci-dessus (qui fonctionne si vous réduisez la taille des données de 8M à quelque chose de plus petit que les limites fifo) utilise déjà des canaux nommés, appelés wcin et wcout. wcin est connecté de té à wc, wcout de wc à chat. Notez que wc est dans ce cas démarré avant l'un ou l'autre - cet ordre n'a pas d'importance, mais ils devraient fonctionner simultanément, donc le &. –

+0

merci beaucoup, même si, comme vous le faites remarquer la solution de tuyau nommé peut être un mauvais moyen d'aller, il sert mes fins à condition de rester en dessous des limites! – ldog

1

Je suppose que dans ce cas, wc attend EOF, et donc il ne reviendra pas (ou sortie d'impression) jusqu'à ce que la première commande est envoyée, alors que awk agit ligne par ligne et sera toujours imprimer en premier. Je ne sais pas si cela est défini lors de l'envoi à d'autres processus.

Pourquoi ne pas simplement compter les lignes avant d'imprimer les lignes?

0

Je ne pense pas que vous pouvez compter là-dessus. Le wc s'exécute ici dans un processus séparé, il n'y a donc pas de synchronisation. Mon essai suggère qu'il pourrait être (au moins dans bash). Comme Daenyth explains, ce cas particulier est spécial, mais essayez-le avec grep -o line au lieu de wc et voyez ce que vous obtenez.

Cela dit, sur mon MacBoox je reçois:

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(grep -o line) | (awk '{print "this is awk: "$0}') 
this is awk: line1 
this is awk: line2 
this is awk: line3 
this is awk: line4 
this is awk: line5 
this is awk: line 
this is awk: line 
this is awk: line 
this is awk: line 
this is awk: line 

très cohérente.Je devrais lire la page de manuel de bash de très près pour être sûr.

De même:

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(awk '{print "*" $0 "*"}') | (awk '{print "this is awk: "$0}') 
this is awk: line1 
this is awk: line2 
this is awk: line3 
this is awk: line4 
this is awk: line5 
this is awk: *line1* 
this is awk: *line2* 
this is awk: *line3* 
this is awk: *line4* 
this is awk: *line5* 

chaque fois ... et

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(awk '{print "*" $0 "*"}') | (grep line) 
line1 
line2 
line3 
line4 
line5 
*line1* 
*line2* 
*line3* 
*line4* 
*line5* 
Questions connexes