Varias tuberías en un registro con cola: la última tubería nunca recibe la entrada estándar

Varias tuberías en un registro con cola: la última tubería nunca recibe la entrada estándar

He entrado en un estado interesante en Ubuntu. Los pasos a continuación lo describen mejor.

Con un solo tubo, estoy viendo lo que espero.

# In shell A
tail -f foo.log | grep aaa

# In shell B
echo aaa >> foo.log

# Shell A prints out `aaa`

Pero con múltiples tuberías no veo nada en absoluto.

# In shell A
tail -f foo.log | grep aaa | grep bbb

# In shell B
echo aaa bbb >> foo.log

# Nothing ever prints in shell A

Pero funciona bien si solo hago eco.

echo 'aaa bbb' | grep aaa | grep bbb

Este es mi intento de crear una reproducción mínima. Originalmente encontré el problema al intentar obtener registros desde adb logcat (herramientas de desarrollo de Android). También lo probé en zsh, bash y fish.

Supuse que tenía algo que ver con mi límite de observador de inotify, pero aumentarlo no cambió nada.

Respuesta1

Esto se debe al almacenamiento en búfer en la tubería, que en general no se preocupa por las líneas y puede acumular datos.

Creo que tail -futiliza el almacenamiento en búfer de línea por sí solo; y el último grepescribe en el tty, por lo que también usa almacenamiento en búfer de línea. Por lo tanto, su primer ejemplo funciona.

Pero grepen el medio es diferente y es necesario ajustar su comportamiento forzando el almacenamiento en búfer de línea o deshabilitando el almacenamiento en búfer. Los siguientes comandos funcionarán como esperaba.

  • Si su grepsoporte --line-buffered(lo hace en Ubuntu):

      tail -f foo.log | grep --line-buffered aaa | grep bbb
    
  • Soluciones más genéricas (funcionarán con muchos filtros distintos de grep):

      tail -f foo.log | unbuffer -p grep aaa | grep bbb
      tail -f foo.log | stdbuf -oL grep aaa | grep bbb
      tail -f foo.log | stdbuf -o0 grep aaa | grep bbb
    

Consulte man 1 grepy para man 1 unbufferobtener man 1 stdbufdetalles y peculiaridades.

Notas:

  • Ninguna de las soluciones es portátil ( grep --line-bufferedy no están especificadas por POSIX).unbufferstdbuf
  • Si puedes hacer esto, grep --line-bufferedentonces debería ser tu elección. No tiene sentido utilizar herramientas adicionales.
  • Pregunta relacionada sobre Unix y Linux SE:Desactivar el almacenamiento en búfer en la tubería.
  • unbufferystdbuf trabajar de maneras completamente diferentes.
  • Con , aquí se debe preferir stdbufel almacenamiento en búfer de línea ( ) a no tener almacenamiento en búfer ( ), porque -oL-o0
    • lo más probable es que funcione mejor,
    • y otras partes de su tubería usan buffering de línea de todos modos.
  • Si grepescribió por última vez en otro archivo, se comportaría como el otro grep. En tal caso, si desea que aparezcan líneas en el archivo final inmediatamente, también debe modificar el comportamiento del último archivo grep.
  • En fish, si grepes una función contenedora, es posible que no obtenga el comportamiento deseado con --line-buffered. Usar command grep --line-buffered. Vea esta pregunta:La tubería de salida espera la entrada de EOFfish.

Nota al margen: tail foo.log | grep aaa | grep bbb(es decir, tailsin -f) no causa el problema porque tailsale. Cuando tailsale, el primero grepdetecta EOF, vacía su búfer y sale, luego el segundo grephace lo mismo.

información relacionada