No tengo mucha experiencia en el uso de tee, así que espero que esto no sea muy básico.
Después de ver una de las respuestas a esta pregunta, me encontré con un extraño comportamiento tee
.
Para poder generar la primera línea y una línea encontrada, puedo usar esto:
ps aux | tee >(head -n1) | grep syslog
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
Sin embargo, la primera vez que ejecuté esto (en zsh) el resultado estaba en el orden incorrecto, los encabezados de columna estaban por debajo de los resultados grep (sin embargo, esto no volvió a ocurrir), así que intenté intercambiar los comandos:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
¡Solo se imprime la primera línea, y nada más! ¿Puedo usar tee para redirigir a grep, o lo estoy haciendo de manera incorrecta?
Mientras escribía esta pregunta, el segundo comando realmente funcionó una vez para mí, lo ejecuté nuevamente cinco veces y luego volví al resultado de una línea. ¿Es este solo mi sistema? (Estoy ejecutando zsh dentro de tmux).
Finalmente, ¿por qué con el primer comando no se muestra "grep syslog" como resultado (solo hay un resultado)?
Para el control aquí está el grep sin el tee
ps aux | grep syslog
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
henry 2290 0.0 0.1 95220 3092 ? Ssl Sep07 3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry 15924 0.0 0.0 3128 824 pts/4 S+ 13:44 0:00 grep syslog
Actualización: Parece que head está causando que todo el comando se trunca (como se indica en la respuesta a continuación), el siguiente comando ahora devuelve lo siguiente:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
ps aux | sed -n -e '1p' -e '/syslog/p'
.Respuestas:
Los comandos
grep
yhead
comienzan aproximadamente al mismo tiempo, y ambos reciben los mismos datos de entrada a su propio ritmo, pero generalmente, a medida que los datos están disponibles. Hay algunas cosas que pueden introducir la salida 'no sincronizada' que cambia las líneas; por ejemplo:Los datos multiplexados de
tee
realmente se envían a un proceso antes que al otro, dependiendo principalmente de la implementación detee
. Unatee
implementación simple tendráread
cierta cantidad de entrada, y luegowrite
dos veces: una vez para stdout y otra para su argumento. Esto significa que uno de esos destinos obtendrá primero los datos.Sin embargo, todas las tuberías están protegidas. Es probable que estos búferes sean de 1 línea cada uno, pero pueden ser más grandes, lo que puede hacer que uno de los comandos de recepción vea todo lo que necesita para la salida (es decir, la
grep
línea ped) antes de que el otro comando (head
) haya recibido datos en todos.A pesar de lo anterior, también es posible que uno de estos comandos reciba los datos pero no pueda hacer nada con ellos a tiempo, y luego el otro comando reciba más datos y los procese rápidamente.
Por ejemplo, incluso si
head
ygrep
son enviados por una línea de datos a la vez, sihead
no sabe cómo tratar con él (o se retrasa por la programación del núcleo),grep
puede mostrar sus resultados anteshead
incluso tiene la oportunidad. Para demostrarlo, intente agregar un retraso:ps aux | tee >(sleep 1; head -n1) | grep syslog
es casi seguro quegrep
primero generará la salida.Creo que a menudo solo obtienes una línea aquí, porque
head
recibe la primera línea de entrada y luego cierra su stdin y sale. Cuandotee
ve que su stdout se ha cerrado, cierra su propio stdin (salida deps
) y sale. Esto podría depender de la implementación.Efectivamente, los únicos datos que
ps
se envían son la primera línea (definitivamente, porquehead
está controlando esto), y tal vez algunas otras líneas anteshead
ytee
cierren sus descriptores stdin.La inconsistencia con la aparición de la segunda línea se introduce por el tiempo:
head
cierra stdin, perops
aún envía datos. Estos dos eventos no están bien sincronizados, por lo que la línea que contienesyslog
todavía tiene la posibilidad de convertirse entee
argumento (elgrep
comando). Esto es similar a las explicaciones anteriores.Puede evitar este problema por completo mediante el uso de comandos que esperan todas las entradas antes de cerrar stdin / salir. Por ejemplo, use en
awk
lugar dehead
, que leerá y procesará todas sus líneas (incluso si no causan salida):Pero tenga en cuenta que las líneas aún pueden aparecer fuera de orden, como se indicó anteriormente, lo que se puede demostrar mediante:
Espero que esto no haya sido demasiado detalle, pero hay muchas cosas simultáneas que interactúan entre sí. Los procesos separados se ejecutan simultáneamente sin ninguna sincronización, por lo que sus acciones en cualquier ejecución particular pueden variar; a veces es útil profundizar en los procesos subyacentes para explicar por qué.
fuente
ps aux | tee >(grep syslog) | head -n1
que dejaría dehead
cerrar stdout? Wow, este comando ha comenzado a dar salida ahora, pero como sucedería de acuerdo con su respuesta, parece truncadoUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
head
. He actualizado la respuesta con este ejemplo:ps aux | tee >(grep syslog) | awk 'NR == 1'
>(cmd)
, el shell crea una tubería con nombre y la pasa como argumento al comando (tee
). Luegotee
está escribiendo en stdout (canalizado aawk
) y también en ese argumento. Es lo mismo quemkfifo a_fifo ; grep ... a_fifo
en un caparazón yps | tee a_fifo | awk ...
en otro.echo >(exit 0)
, que hará eco del argumento real pasado por el shell (en mi caso, se convierte/dev/fd/63
). Esto debería funcionar igual en bash y zsh.grep syslog
no siempre se muestra ya que depende del tiempo. Cuando utiliza la canalización de shell, está ejecutando comandos casi simultáneamente. Pero la clave aquí es la palabra "casi". Sips
termina de escanear todos los procesos antes de iniciar grep, no estará en la lista. Puede obtener resultados aleatorios dependiendo de la carga del sistema, etc.Algo similar sucede con tu tee. Se ejecuta en segundo plano en subshell y puede dispararse antes o después de grep. Es por eso que el orden de salida es inconsistente.
En cuanto a la pregunta de salida, su comportamiento es bastante extraño. Esto se debe a que no se usa de la manera normal. Se ejecuta sin ningún argumento, lo que significa que solo debe copiar los datos de su stdin a stdout. Pero su stdout se redirige a la cabeza de ejecución del subshell (en el primer caso) o grep (segundo caso). Pero también se canaliza al siguiente comando. Creo que lo que sucede en este caso depende de la implementación. Por ejemplo, en mi bash 4.2.28, nunca se escribe nada en subshell stdin. En zsh, funciona de manera confiable como desea (imprimiendo tanto la primera línea de ps como las líneas buscadas), cada vez que lo intento,
fuente
Un poco hack, pero aquí está mi solución, en forma de una
psgrep()
función de shell que uso:Redireccione la
ps
fila de encabezado aSTDERR
, luegogrep
enSTDOUT
, pero primero elimine elgrep
comando en sí, para evitar que la fila de "ruido" surja degrep
sí misma:fuente