Dado este ejemplo mínimo
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
sale LINE 1
y luego, después de un segundo, sale LINE 2
, como se esperaba .
Si canalizamos esto a grep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
El comportamiento es el mismo que en el caso anterior, como se esperaba .
Si, alternativamente, canalizamos esto a cat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
El comportamiento es nuevamente el mismo, como se esperaba .
Sin embargo , si nos dirigimos a grep LINE
, y luego a cat
,
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
no hay salida hasta que pasa un segundo, y ambas líneas aparecen en la salida de inmediato, lo que no esperaba .
¿Por qué sucede esto y cómo puedo hacer que la última versión se comporte de la misma manera que los primeros tres comandos?
cat
concatena archivos ¿Qué estás tratando de hacer entrandocat
?cat
simplemente se leestdin
y se genera enstdout
. Por supuesto, se me ocurrió esta pregunta con muchas cosas complejas en lugar deecho
ycat
, pero resultaron ser irrelevantes, ya que el problema aparece con ejemplos mucho más simples.Respuestas:
Cuando
grep
la salida de (al menos GNU) no es una terminal, almacena su salida, que es lo que causa el comportamiento que estás viendo. Puede deshabilitar esto usandogrep
la--line-buffered
opción de GNU :o la
stdbuf
utilidad:Apagar el almacenamiento en búfer en la tubería tiene más información sobre este tema.
fuente
Explicación simplificada
Al igual que muchas utilidades, esto no es algo peculiar de un programa,
grep
varía su salida estándar entre estar en línea y completamente en memoria intermedia . En el primer caso, la biblioteca C almacena en memoria intermedia los datos de salida en la memoria hasta que se llena el búfer que contiene esos datos o se agrega un carácter de salto de línea (o el programa finaliza limpiamente), por lo que llamawrite()
para escribir realmente el contenido del búfer. En el último caso, solo el búfer en memoria que se llena (o el programa termina limpiamente) activa elwrite()
.Explicación más detallada
Esta es la explicación bien conocida, pero ligeramente incorrecta. De hecho, la salida estándar no es amortiguada línea, pero inteligente amortiguada en la biblioteca GNU C y C biblioteca de BSD. La salida estándar también se vacía cuando la lectura de la entrada estándar agota su búfer en memoria (de entrada previa a la lectura) y la biblioteca C tiene que llamar
read()
para obtener más entrada y está leyendo el comienzo de una nueva línea. (Una razón para esto es evitar el punto muerto cuando otro programa se conecta a ambos extremos de un filtro y espera poder operar línea por línea, alternando entre escribir en el filtro y leerlo; como "coprocesos" en GNUawk
por ejemplo.)Influencia de la biblioteca C
grep
y las otras utilidades hacen esto, o más estrictamente, las bibliotecas C que usan hacen esto, porque esta es una característica definida de programación en el lenguaje C, en función de lo que detectan que es su salida estándar. Si (y solo si) no es un dispositivo interactivo, eligen el almacenamiento en búfer completo, de lo contrario eligen el almacenamiento en búfer inteligente. Se considera que una tubería no es un dispositivo interactivo, porque la definición de ser un dispositivo interactivo, al menos en el mundo de Unix y Linux, es esencialmente laisatty()
devolución de la llamada verdadera para el descriptor de archivo relevante.Soluciones para deshabilitar el almacenamiento en búfer completo
Algunas utilidades como
grep
tienen opciones idiosincrásicas como--line-buffered
que cambian esta decisión, que como puede ver está mal nombrada. Pero una fracción cada vez más pequeña de los programas de filtro que uno podría usar realmente tiene esa opción.En términos más generales, se pueden usar herramientas que profundizan en los componentes internos específicos de la biblioteca C y cambian su toma de decisiones (que tienen problemas de seguridad si el programa que se va a modificar es set-UID, y también son específicos de bibliotecas C particulares, y de hecho son específico para programas escritos o en capas sobre el lenguaje C), o herramientas como esas
ptybandage
que no cambian las partes internas del programa sino que simplemente interponen un pseudo-terminal como salida estándar para que la decisión salga como "interactiva", para afectar estoOtras lecturas
fuente
grep
, sino de las llamadas de la biblioteca subyacente,setbuf
/setvbuf
. No conozco una referencia en línea confiable para el estándar C, pero, por ejemplo, las páginas de manual de Linux y FreeBSD junto con la descripción POSIX de losetvbuf
llaman "buffer de línea". Incluso la constante simbólica es_IOLBF
.Utilizar
para hacer que grep no almacene más de una línea a la vez.
fuente