He canalizado una línea en el script bash y quiero verificar si la tubería tiene datos, antes de alimentarla a un programa.
Buscando encontré test -t 0
pero no funciona aquí. Siempre devuelve falso. Entonces, ¿cómo estar seguro de que la tubería tiene datos?
Ejemplo:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
Salida: fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
Salida: fill
¿A diferencia de la forma estándar / canónica de probar si la tubería anterior produjo salida? la entrada necesita ser preservada para pasarla al programa. Esto generaliza ¿Cómo canalizar la salida de un proceso a otro pero solo se ejecuta si el primero tiene salida? que se centra en enviar correos electrónicos.
Respuestas:
No hay forma de ver el contenido de una tubería utilizando utilidades de shell comúnmente disponibles, ni hay una manera de leer un carácter en la tubería y luego volver a colocarlo. La única forma de saber que una tubería tiene datos es leer un byte, y luego debe llevar ese byte a su destino.
Así que haz eso: lee un byte; si detecta un final de archivo, haga lo que quiera hacer cuando la entrada esté vacía; si lee un byte, bifurque lo que quiere hacer cuando la entrada no esté vacía, canalice ese byte en él y canalice el resto de los datos.
La
ifne
utilidad de moreutils de Joey Hess ejecuta un comando si su entrada no está vacía. Por lo general, no se instala de manera predeterminada, pero debería estar disponible o ser fácil de construir en la mayoría de las variantes de Unix. Si la entrada está vacía,ifne
no hace nada y devuelve el estado 0, que no se puede distinguir del comando que se ejecuta correctamente. Si desea hacer algo si la entrada está vacía, debe hacer arreglos para que el comando no devuelva 0, lo que puede hacerse haciendo que el caso de éxito devuelva un estado de error distinguible:test -t 0
no tiene nada que ver con esto; prueba si la entrada estándar es un terminal. No dice nada de una forma u otra sobre si hay alguna entrada disponible.fuente
peek
que pueda devolver los datos reales de una tubería, no solo la cantidad que hay. (en 4.4 BSD, 386BSD, etc., las tuberías se implementaron como pares de zócalos , pero eso fue destripado en versiones posteriores de * BSD, aunque las mantuvieron bidireccionales).read -t 0
(t en este caso significa tiempo de espera, si se pregunta).Una solución simple es usar el
ifne
comando (si la entrada no está vacía). En algunas distribuciones, no está instalado por defecto. Es una parte del paquetemoreutils
en la mayoría de las distribuciones.ifne
ejecuta un comando dado si y solo si la entrada estándar no está vacíaTenga en cuenta que si la entrada estándar no está vacía, se pasa
ifne
al comando dadofuente
Antigua pregunta, pero en caso de que alguien la encuentre como yo: mi solución es leer con tiempo de espera.
Si
stdin
está vacío, esto regresará después de 5 segundos. De lo contrario, leerá toda la entrada y podrá procesarla según sea necesario.fuente
-t
lamentablemente no forma parte de POSIX: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.htmlcompruebe si el descriptor de archivo de stdin (0) está abierto o cerrado:
fuente
[ -t 0 ]
comprueba si fd 0 está abierto en un tty , no si está cerrado o abierto../that_script </dev/null
=> "stdin tiene datos". O./that_script <&-
tener el stdin realmente cerrado .También puede usar
test -s /dev/stdin
(en una subshell explícita).fuente
En bash:
Detecta si una entrada tiene datos (sin leer nada). Luego puede leer la entrada (si la entrada está disponible en el momento en que se ejecuta la lectura):
Nota: Comprenda que esto depende del tiempo. Esto detecta si la entrada ya tiene datos solo en el momento en que se
read -t
ejecuta.Por ejemplo, con
la salida es
1
(falla de lectura, es decir: entrada vacía). El eco escribe algunos datos, pero no es muy rápido para iniciar y escribir su primer byte, por lo tanto,read -t 0
informará que su entrada está vacía, ya que el programa aún no ha escrito nada.fuente
bash
hará unaselect()
o unaioctl(FIONREAD)
, o ninguna de ellas, pero no ambas, como debería para que funcione.read -t0
está roto. No lo useUna manera fácil de verificar si hay datos disponibles para leer en Unix es con el
FIONREAD
ioctl.No puedo pensar en ninguna utilidad estándar que haga exactamente eso, así que aquí hay un programa trivial que lo hace (mejor que el
ifne
de moreutils en mi humilde opinión ;-)).Si no hay datos disponibles en stdin, saldrá con el estado 1. Si hay datos, se ejecutará
prog
. Si noprog
se da, saldrá con el estado 0.Puede eliminar la
poll
llamada si solo le interesan los datos disponibles de inmediato . Esto debería funcionar con la mayoría de los tipos de fds, no solo con tuberías.fionread.c
fuente
POLLHUP
evento también para manejar el caso vacío? ¿Funciona eso si hay varias descripciones de archivos en el otro extremo de la tubería?Si te gustan las frases cortas y crípticas:
Usé los ejemplos de la pregunta original. Si no desea la
-q
opción de uso de datos canalizados con grepfuente
Esto parece ser una implementación razonable de ifne en bash si estás de acuerdo con leer toda la primera línea
fuente
read
también devolverá falso si la entrada no está vacía pero no contiene caracteres de nueva línea,read
realiza algún procesamiento en su entrada y puede leer más de una línea a menos que la llame comoIFS= read -r line
.echo
no se puede usar para datos arbitrarios.Esto funciona para mí usando
read -rt 0
ejemplo de la pregunta original, sin datos:
fuente
{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
(falso negativo) ytrue | { sleep .1; read -rt0 && echo YES; }
(falso positivo). De hecho, Bashread
se deje engañar incluso por FDS abiertas en sólo escritura modo:{ read -rt0 && echo YES; cat; } 0>/tmp/foo
. Lo único que parece hacer esselect(2)
en ese fd.select
devolverá un fd como "listo" si unread(2)
en él no se bloquea, no importa si regresaráEOF
o si se produjo un error. Conclusión:read -t0
se rota enbash
. No lo uses{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
no es un falso negativo porque (en el momento en que se ejecuta la lectura) no hay entrada. Más tarde (suspensión .1) esa entrada está disponible (para el gato).echo "" | { read -t0 && echo YES; }
imprime SÍ peroecho "" | { read -rt0 && echo YES; }
no lo hace.