Besoin d'explications de la part des utilisateurs expérimentés pour un tel comportement imprévisible:
ps -eF | { head -n 1;grep worker; }
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
root 441 2 0 0 0 2 paź15 ? 00:00:00 [kworker/2:1H]
tout semble ok alors
ls -la / | { head -n 1;grep sbin; }
affiche uniquement la sortie de head
... J'ai réfléchi stdout 2>&1
et ne fonctionne pas non plus pour moi c'est bizarre, des explications ou suggérer comment le gérer?
head
etgrep
ne faites rien là-bas.Réponses:
J'ai fait des recherches sur l'utilisation
strace
et cela semble être dû à la façon dont le programme sur le côté gauche du pipeline écrit sur le terminal. Lorsque lals
commande est exécutée, elle écrit toutes les données en un seulwrite()
. Cela faithead
consommer tout stdin.D'un autre côté,
ps
écrit les données par lots, donc seul le premierwrite()
est consommé parhead
, puis il existe. Les appels ultérieurs àwrite()
iront augrep
processus nouvellement généré .Cela signifie que cela ne fonctionnerait pas si le processus pour lequel vous essayez de
grep
ne se produisait pas dans le premierwrite()
, car ilgrep
ne voit pas toutes les données (il voit encore moins que les données moins la première ligne).Voici un exemple de tentative de grep pour le pid 1 sur mon système:
Votre
ps -eF
exemple ne fonctionne que par hasard.la source
write()
appels. Si l' appelhead
était lent à exécuter sonread()
appel (de sorte que le tampon de canal contenait toutes les données), il présenterait le même comportement à la fois surls
etps
.Cela est dû à la mise en mémoire tampon dans la glibc. Dans le cas de
ls
la sortie est dans un tampon interne et en tant que tel est passé juste àhead
. Pour leps -eF
, la sortie est plus grande et donc une foishead
terminée, ce qui suitgrep
obtient les parties restantes (mais pas la totalité) de la sortie deps
.Vous pouvez vous en débarrasser en décompressant le tube - par exemple avec
sed -u
(je ne suis pas sûr que ce ne soit pas une extension GNU):la source
Ce qui se passe, c'est qu'il
head -n 1
lit plus d'une ligne. Pour un débit optimal, head lit des morceaux d'octets, il peut donc lire 1024 octets à la fois, puis rechercher dans ces octets le premier saut de ligne. Étant donné que le saut de ligne peut se produire au milieu de ces 1024 octets, le reste des données est perdu. Il ne peut pas être remis sur le tuyau. Ainsi, le prochain processus qui s'exécute n'obtient que 1025 octets et plus.Votre première commande réussit parce que le
kworker
processus est après ce premier morceau quihead
lit.Pour que cela fonctionne, il
head
faudrait lire 1 caractère à la fois. Mais c'est extrêmement lent, donc ce n'est pas le cas.La seule façon de faire quelque chose comme ça efficacement est d'avoir un seul processus à la fois "head" et "grep".
Voici deux façons de procéder:
ou
Il y a beaucoup plus...
la source
Si vous ne voulez que la première ligne ou deux, le type d'astuce suivant fonctionne et évite les problèmes de mise en mémoire tampon causés par l'utilisation de deux commandes différentes pour lire le flux de sortie:
Le
read
est intégré au shell et ne consomme pas tout un tampon d'entrée juste pour sortir la ligne, donc l'utilisationread
laisse tout le reste de la sortie pour la commande suivante.Si vous souhaitez accentuer les problèmes de mise en mémoire tampon illustrés par vos exemples qui utilisent deux commandes différentes, ajoutez-y un
sleep
pour éliminer les problèmes de synchronisation et autorisez la commande de gauche à générer toutes ses sorties avant que les commandes de droite n'essaient de lire il:Maintenant, les deux exemples ci-dessus échouent de la même manière - le
head
lit un tampon entier de la sortie juste pour produire la seule ligne, et ce tampon n'est pas disponible pour les suivantsgrep
.Vous pouvez voir le problème de mise en mémoire tampon encore plus clairement en utilisant quelques exemples qui numérotent les lignes de sortie afin que vous puissiez savoir quelles lignes sont manquantes:
Un moyen simple de voir le problème de mise en mémoire tampon est d'utiliser
seq
un outil qui génère une liste de nombres. Nous pouvons facilement identifier les numéros manquants:Ma solution astucieuse utilisant le shell pour lire et faire écho à la première ligne fonctionne correctement même avec le retard de sommeil ajouté:
Ci-dessous est un exemple complet montrant les
head
problèmes de mise en mémoire tampon, montrant commenthead
consomme une mémoire tampon entière de la sortie juste pour produire ses cinq lignes à chaque fois. Ce tampon consommé n'est pas disponible pour lahead
commande suivante de la séquence:En regardant le nombre
1861
ci-dessus, nous pouvons calculer la taille du tampon utiliséhead
en comptant laseq
sortie de1
à1860
:Nous voyons que cela
head
met en mémoire tampon en lisant un 8 Ko complet (8 * 1024 octets) de la sortie du tuyau à la fois, même pour produire seulement quelques lignes de sa propre sortie.la source