Je ne comprends pas comment les données circulent dans le pipeline et j'espère que quelqu'un pourrait clarifier ce qui se passe là-bas.
Je pensais qu'un pipeline de commandes traite les fichiers (texte, tableaux de chaînes) ligne par ligne. (Si chaque commande elle-même fonctionne ligne par ligne.) Chaque ligne de texte passe par le pipeline, les commandes n'attendent pas que la précédente termine le traitement de l'entrée entière.
Mais il semble que ce ne soit pas le cas.
Voici un exemple de test. Il y a quelques lignes de texte. Je les mets en majuscule et répète deux fois chaque ligne. Je le fais avec cat text | tr '[:lower:]' '[:upper:]' | sed 'p'
.
Pour suivre le processus, nous pouvons l'exécuter "de manière interactive" - sautez le nom du fichier d'entrée dans cat
. Chaque partie du pipeline fonctionne ligne par ligne:
$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2
Mais le pipeline complet attend que je termine l'entrée avec EOF
et n'imprime alors que le résultat:
$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D
Est-ce censé en être ainsi? Pourquoi n'est-ce pas ligne par ligne?
cat
mise en mémoire tampon jusqu'à la fermeture de stdin.tr
etsed
ne traite les lignes d'cat
avant la fermeture de stdinRéponses:
Il existe une règle générale de mise en mémoire tampon suivie de la bibliothèque d'E / S standard C (
stdio
) que la plupart des programmes Unix utilisent. Si la sortie va vers un terminal, elle est vidée à la fin de chaque ligne; sinon, il n'est vidé que lorsque le tampon (8K sur mon système Linux / amd64; pourrait être différent sur le vôtre) est plein.Si tous les services publics suivaient la règle générale, vous verriez la sortie retardée dans tous vos exemples (
cat|sed
,cat|tr
etcat|tr|sed
). Mais il y a une exception: GNUcat
ne met jamais sa sortie en mémoire tampon. Soit il n'utilise pas,stdio
soit il modifie lastdio
politique de mise en mémoire tampon par défaut .Je peux être sûr que vous utilisez GNU
cat
et pas un autre Unixcat
parce que les autres ne se comporteraient pas de cette façon. Unix traditionnelcat
a une-u
option pour demander une sortie sans tampon. GNUcat
ignore l'-u
option car sa sortie est toujours sans tampon.Ainsi, chaque fois que vous avez un tuyau avec un
cat
à gauche, dans le système GNU, le passage des données à travers le tuyau ne sera pas retardé. Lecat
ne va même pas ligne par ligne - votre terminal le fait. Pendant que vous tapez une entrée pour cat, votre terminal est en mode "canonique" - basé sur la ligne, avec des touches d'édition comme backspace et ctrl-U vous offrant la possibilité de modifier la ligne que vous avez tapée avant de l'envoyer Enter.Dans l'
cat|tr|sed
exemple,tr
reçoit toujours des donnéescat
dès que vous appuyez sur Enter, maistr
suit lastdio
stratégie par défaut: sa sortie va dans un tube, donc elle ne videra pas après chaque ligne. Il écrit dans le deuxième canal lorsque le tampon est plein ou lorsqu'un EOF est reçu, selon la première éventualité.sed
suit également lastdio
politique par défaut, mais sa sortie va à un terminal, il écrira donc chaque ligne dès qu'il en aura fini avec elle. Cela a un effet sur la quantité que vous devez taper avant que quelque chose n'apparaisse à l'autre extrémité du pipeline - si lased
mise en mémoire tampon de sa sortie était bloquée, vous devriez taper deux fois plus (pour remplirtr
le tampon de sortie etsed
la sortie de tampon).GNU
sed
a une-u
option, donc si vous inversez l'ordre et l'utilisez,cat|sed -u|tr
vous verrez la sortie apparaître de nouveau instantanément. (L'sed -u
option pourrait être disponible ailleurs mais je ne pense pas que ce soit une ancienne tradition unix commecat -u
) Pour autant que je sache, il n'y a pas d'option équivalente pourtr
.Il existe un utilitaire appelé
stdbuf
qui vous permet de modifier le mode de mise en mémoire tampon de toute commande qui utilise lesstdio
valeurs par défaut. C'est un peu fragile car il sertLD_PRELOAD
à accomplir quelque chose que la bibliothèque C n'a pas été conçue pour prendre en charge, mais dans ce cas, cela semble fonctionner:la source
tee
etdd
jouent généralement selon leurs propres règles. Lorsqu'ils sont combinés de manière imaginative, les trois outils peuvent à peu près de façon portative annuler tout besoin destdbuf
pipelines en arrière-plan.En fait, cela m'a pris un peu de réflexion pour comprendre et encore plus pour répondre. Grande question (je voterai ensuite).
Vous avez négligé d'essayer
tr | sed
dans vos éléments de débogage ci-dessus:Donc évidemment des
tr
tampons. Apprendre quelque chose de nouveau chaque jour!MODIFIER :
En y réfléchissant, nous avons isolé la cause, mais sans fournir d'explication. Si vous
cat | tr
, il écrit tout de suite, si vouscat | sed
, il écrit tout de suite, mais si voustr | sed
, il attend pourEOF
. Je suggère que la réponse pourrait être enterré danstr
ou lesed
code source alors, et non un problème de tuyauterie.MODIFIER :
Je vois que Wumpus a fourni l'explication pendant que je tapais la dernière modification. Merci!
la source
stdbuf
ce qui pourrait également être utile. unix.stackexchange.com/questions/182537/…