Necesito explicaciones de los usuarios avanzados para un comportamiento tan impredecible:
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]
todo se ve bien mientras que
ls -la / | { head -n 1;grep sbin; }
muestra solo la salida de head
... Pensé stdout 2>&1
y no funciona ni para mí es extraño, ¿alguna explicación o sugerencia de cómo manejarlo?
head
ygrep
no hacer nada allí.Respuestas:
Investigué un poco usando
strace
y parece deberse a la forma en que el programa en el lado izquierdo de la tubería está escribiendo en la terminal. Cuandols
se ejecuta el comando, escribe todos los datos en un solowrite()
. Esto hacehead
que se consuma todo el stdin.Por otro lado,
ps
escribe datos en lotes, por lo que solo el primerowrite()
es consumido porhead
, y luego existe. Las llamadas posteriores awrite()
irán algrep
proceso recién generado .Esto significa que no funcionaría si el proceso que está intentando
grep
no se produjo en el primerowrite()
, yagrep
que no puede ver todos los datos (ve incluso menos que solo los datos menos la primera línea).Aquí hay un ejemplo de cómo intentar grep para pid 1 en mi sistema:
Tu
ps -eF
ejemplo solo funciona por casualidad.fuente
write()
llamadas. Sihead
tardaran en realizar suread()
llamada (de modo que el búfer de la tubería tuviera todos los datos), exhibiría el mismo comportamiento en ambosls
yps
.Esto es causado por el almacenamiento en búfer en glibc. En el caso de
ls
la salida está en un búfer interno y como tal se pasa solo ahead
. Para elps -eF
, la salida es más grande y, por lo tanto, una vez quehead
finaliza, lo siguientegrep
obtiene las partes restantes de (pero no la totalidad) de la salidaps
.Puede deshacerse de él desarmando la tubería, por ejemplo con
sed -u
(no estoy seguro de que no sea una extensión GNU):fuente
Lo que sucede es que
head -n 1
lee más de 1 línea. Para un rendimiento óptimo, la cabeza lee fragmentos de bytes, por lo que podría leer 1024 bytes a la vez, y luego mirar a través de esos bytes para el primer salto de línea. Dado que el salto de línea puede ocurrir en el medio de esos 1024 bytes, se pierde el resto de los datos. No se puede volver a poner en la tubería. Entonces, el siguiente proceso que se ejecuta solo obtiene los bytes 1025 y más.Su primer comando tiene éxito porque el
kworker
proceso es posterior a ese primer fragmento que sehead
lee.Para que esto funcione,
head
tendría que leer 1 carácter a la vez. Pero esto es extremadamente lento, por lo que no lo hace.La única forma de hacer algo como esto de manera eficiente es hacer que un solo proceso haga tanto la "cabeza" como la "grep".
Aquí hay 2 formas de hacer esto:
o
Hay muchos más ...
fuente
Si solo desea la primera o dos líneas, el siguiente tipo de truco funciona y evita los problemas de almacenamiento en búfer causados por el uso de dos comandos diferentes para leer la secuencia de salida:
El
read
está integrado en el shell y no consume un búfer completo de entrada solo para generar una línea, por lo que el usoread
deja todo el resto de la salida para el siguiente comando.Si desea acentuar los problemas de almacenamiento en búfer que muestran sus ejemplos que usan dos comandos diferentes, agréguelos
sleep
a ellos para eliminar los problemas de sincronización y permita que el comando de la izquierda genere toda su salida antes de que los comandos de la derecha intenten leer cualquiera de eso:Ahora, los dos ejemplos anteriores fallan de la misma manera:
head
lee un búfer completo de la salida solo para producir una línea, y ese búfer no está disponible para lo siguientegrep
.Puede ver el problema del almacenamiento en búfer aún más claramente utilizando algunos ejemplos que numeran las líneas de salida para que pueda saber qué líneas faltan:
Una forma sencilla de ver el problema del almacenamiento en búfer es utilizar
seq
un generador de una lista de números. Podemos decir fácilmente qué números faltan:Mi solución truco usando el shell para leer y hacer eco de la primera línea funciona correctamente incluso con el retraso de sueño agregado:
A continuación se muestra un ejemplo completo que muestra los
head
problemas de almacenamiento en búfer, que muestra cómohead
consume un búfer completo de la salida solo para producir sus cinco líneas cada vez. Ese búfer consumido no está disponible para el siguientehead
comando en la secuencia:Mirando el número
1861
anterior, podemos calcular el tamaño del búfer que se utilizahead
contando laseq
salida de1
a1860
:Vemos que
head
se almacena en búfer leyendo 8 KB completos (8 * 1024 bytes) de la salida de la tubería a la vez, incluso para producir solo unas pocas líneas de su propia salida.fuente