J'ai un script qui appelle deux commandes:
long_running_command | print_progress
Le long_running_command
imprime les progrès mais je n'en suis pas satisfait. J'utilise print_progress
pour le rendre plus agréable (à savoir, j'imprime la progression sur une seule ligne).
Le problème: La connexion d'un tube à stdout active également un tampon 4K, donc le programme d'impression Nice ne reçoit rien ... rien ... rien ... dans son ensemble beaucoup ... :)
Comment désactiver le 4K
tampon pour le long_running_command
(non, je n'ai pas la source)?
Vous pouvez utiliser la commande unbuffer
(qui fait partie du package expect
), par exemple.
unbuffer long_running_command | print_progress
unbuffer
se connecte à long_running_command
via un pseudoterminal (pty), ce qui oblige le système à le traiter comme un processus interactif, n'utilisant donc pas la mise en mémoire tampon de 4 kio dans le pipeline qui est la cause probable du retard.
Pour les pipelines plus longs, vous devrez peut-être annuler la mise en mémoire tampon de chaque commande (sauf la dernière), par exemple.
unbuffer x | unbuffer -p y | z
Une autre façon d'habiller ce chat est d'utiliser le programme stdbuf
, qui fait partie du GNU Coreutils (FreeBSD a aussi le sien)).
stdbuf -i0 -o0 -e0 command
Cela désactive complètement la mise en mémoire tampon des entrées, des sorties et des erreurs. Pour certaines applications, la mise en mémoire tampon de ligne peut être plus appropriée pour des raisons de performances:
stdbuf -oL -eL command
Notez que cela ne fonctionne que pour la mise en mémoire tampon stdio
(printf()
, fputs()
...) pour les applications liées dynamiquement, et uniquement si cette application n'ajuste pas autrement la mise en mémoire tampon de ses flux standard par lui-même, bien que cela devrait couvrir la plupart des applications.
Encore une autre façon d'activer le mode de sortie de mise en mémoire tampon de ligne pour le long_running_command
consiste à utiliser la commande script
qui exécute votre long_running_command
dans un pseudo terminal (pty).
script -q /dev/null long_running_command | print_progress # FreeBSD, Mac OS X
script -c "long_running_command" /dev/null | print_progress # Linux
Pour grep
, sed
et awk
, vous pouvez forcer la sortie à être mise en mémoire tampon de ligne. Vous pouvez utiliser:
grep --line-buffered
Force la sortie à être mise en mémoire tampon de ligne. Par défaut, la sortie est mise en mémoire tampon de ligne lorsque la sortie standard est une borne et un bloc en mémoire tampon dans le cas contraire.
sed -u
Rendre la ligne de sortie en mémoire tampon.
Voir cette page pour plus d'informations: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html
S'il s'agit d'un problème avec la libc modifiant sa mise en mémoire tampon/vidage lorsque la sortie ne va pas à un terminal, vous devriez essayer socat . Vous pouvez créer un flux bidirectionnel entre presque tout type de mécanisme d'E/S. L'un d'eux est un programme fourchu parlant à un pseudo tty.
socat EXEC:long_running_command,pty,ctty STDIO
Ce qu'il fait c'est
Si cela vous donne la même sortie que long_running_command
, vous pouvez continuer avec un tuyau.
Edit: Wow Vous n'avez pas vu la réponse sans tampon! Eh bien, socat est un excellent outil de toute façon, donc je pourrais simplement laisser cette réponse
Vous pouvez utiliser
long_running_command 1>&2 |& print_progress
Le problème est que libc mettra le tampon en ligne lors de la sortie standard vers l'écran et le tampon complet lors de la sortie standard vers un fichier. Mais pas de tampon pour stderr.
Je ne pense pas que ce soit le problème avec le buffer de pipe, c'est tout à propos de la politique de buffer de libc.
Auparavant, et c'est probablement toujours le cas, lorsque la sortie standard est écrite sur un terminal, elle est mise en mémoire tampon par défaut - lorsqu'une nouvelle ligne est écrite, la ligne est écrite sur le terminal. Lorsque la sortie standard est envoyée à un canal, elle est entièrement tamponnée - les données ne sont donc envoyées au processus suivant dans le pipeline que lorsque le tampon d'E/S standard est rempli.
C'est la source du problème. Je ne sais pas s'il y a beaucoup à faire pour y remédier sans modifier l'écriture du programme dans le canal. Vous pouvez utiliser la fonction setvbuf()
avec l'indicateur _IOLBF
Pour mettre inconditionnellement stdout
en mode ligne tampon. Mais je ne vois pas de moyen facile d'imposer cela sur un programme. Ou le programme peut faire fflush()
aux points appropriés (après chaque ligne de sortie), mais le même commentaire s'applique.
Je suppose que si vous remplaciez le tube par un pseudo-terminal, la bibliothèque d'E/S standard penserait que la sortie était un terminal (car c'est un type de terminal) et alignerait automatiquement le tampon. C'est cependant une façon complexe de gérer les choses.
Je sais que c'est une vieille question et a déjà eu beaucoup de réponses, mais si vous souhaitez éviter le problème de tampon, essayez simplement quelque chose comme:
stdbuf -oL tail -f /var/log/messages | tee -a /home/your_user_here/logs.txt
Cela produira en temps réel les journaux et les enregistrera également dans le logs.txt
et le tampon n'affectera plus le tail -f
commande.
Je ne pense pas que le problème soit avec le tuyau. Il semble que votre processus de longue durée ne nettoie pas suffisamment son propre tampon. Changer la taille de la mémoire tampon du tube serait un hack pour le contourner, mais je ne pense pas que ce soit possible sans reconstruire le noyau - quelque chose que vous ne voudriez pas faire en tant que hack, car cela affecte probablement beaucoup d'autres processus.
Dans la même veine que réponse de chad , vous pouvez écrire un petit script comme celui-ci:
# save as ~/bin/scriptee, or so
script -q /dev/null sh -c 'exec cat > /dev/null'
Utilisez ensuite cette commande scriptee
en remplacement de tee
.
my-long-running-command | scriptee
Hélas, je n'arrive pas à obtenir une version comme celle-ci pour fonctionner parfaitement sous Linux, semble donc limitée aux Unix de style BSD.
Sous Linux, c'est proche, mais vous ne récupérez pas votre invite à la fin (jusqu'à ce que vous appuyiez sur Entrée, etc.) ...
script -q -c 'cat > /proc/self/fd/1' /dev/null
Selon ce post ici , vous pouvez essayer de réduire le canal ulimit à un seul bloc de 512 octets. Cela ne désactivera certainement pas la mise en mémoire tampon, mais bien, 512 octets est bien moins que 4K: 3
J'ai trouvé cette solution intelligente: (echo -e "cmd 1\ncmd 2" && cat) | ./Shell_executable
Cela fait l'affaire. cat
lira une entrée supplémentaire (jusqu'à EOF) et la transmettra au tube après que echo
aura placé ses arguments dans le flux d'entrée de Shell_executable
.
Selon this la taille du buffer de pipe semble être définie dans le noyau et vous obligerait à recompiler votre noyau pour le modifier.