¿Cómo verificar si stdin es / dev / null desde el shell?

9

En Linux, ¿hay alguna forma de que un script de shell compruebe si su entrada estándar se redirige desde el dispositivo nulo (1, 3) * , idealmente sin leer nada?

El comportamiento esperado sería:

./checkstdinnull
-> no
./checkstdinnull < /dev/null
-> yes
echo -n | ./checkstdinnull
-> no

EDIT

mknod secretunknownname c 1 3
exec 6<secretunknownname
rm secretunknownname
./checkstdinnull <&6
-> yes

Sospecho que "solo" necesito leer el número de may / min del dispositivo de entrada . Pero no puedo encontrar una manera de hacerlo desde el shell.


* No es necesario solo /dev/null, pero cualquier dispositivo nulo, incluso si se creó manualmente con mknod.

Sylvain Leroux
fuente
2
¿Necesita saber si es /dev/null, o simplemente que no es un tty?
roaima
La salida de { readlink -f /dev/stdin; } <&6para el caso donde usó exec y eliminó el nodo es /root/secretunknownname (deleted). Como muestra que el archivo se eliminó: ¿no es suficiente para lo que necesita?
Isaac
Yo necesito (en realidad "necesario") para saber si la entrada estándar fue el dispositivo nulo.
Sylvain Leroux
2
Estoy tratando de encontrar mi camino en un sistema industrial (¡muy mal diseñado!). Y a veces asigna la entrada de la utilidad del trabajador al dispositivo nulo o, en otras ocasiones, al dispositivo de hardware real. En ambos casos usando el mismo código, pero con diferentes números de desarrollo mayor / menor. Estamos tratando de rastrear cuándo (¡y por qué!) A veces eligió usar uno u otro. Por ahora, la statsolución es la única que funciona.
Sylvain Leroux
"Entonces, el ejemplo en su EDIT realmente no reproduce el problema que usted describe, o: ¿lo hace?" Lo hace. Entrada en redirigido desde el dispositivo nulo . A la que generalmente se accede /dev/null, pero no es necesario. Puede "alias" con mknods ilustrado en mi ejemplo.
Sylvain Leroux

Respuestas:

18

En Linux, puedes hacerlo con:

stdin_is_dev_null(){ test "`stat -Lc %t:%T /dev/stdin`" = "`stat -Lc %t:%T /dev/null`"; }

En un Linux sin stat (1) (p. Ej., Busybox en su enrutador):

stdin_is_dev_null(){ ls -Ll /proc/self/fd/0 | grep -q ' 1,  *3 '; }

En * bsd:

stdin_is_dev_null(){ test "`stat -f %Z`" = "`stat -Lf %Z /dev/null`"; }

En sistemas como * BSD y Solaris, /dev/stdin, /dev/fd/0y /proc/PID/fd/0no son enlaces simbólicos "mágicas" como en Linux, pero los dispositivos de caracteres que colocarán en el archivo real, cuando está abierto . Una estadística (2) en su ruta devolverá algo diferente a una fstat (2) en el descriptor de archivo abierto.

Esto significa que el ejemplo de Linux no funcionará allí, incluso con GNU coreutils instalado. Si las versiones de GNU stat (1) son lo suficientemente recientes, puede usar el -argumento para dejar que haga un fstat (2) en el descriptor de archivo 0, al igual que stat (1) de * bsd:

stdin_is_dev_null(){ test "`stat -Lc %t:%T -`" = "`stat -Lc %t:%T /dev/null`"; }

También es muy fácil hacer la verificación de forma portátil en cualquier idioma que ofrezca una interfaz para fstat (2), por ejemplo. en perl:

stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
Mosvy
fuente
1
Como el tipo de dispositivo /dev/nulles 1:3, puede probarlo de inmediato.
RudiC
12
FWIW la pregunta no era sobre la sintaxis de shell. En lo que respecta a mi problema, si la respuesta solo me hubiera señalado el statcomando, ya habría estado bastante satisfecho. No veo el punto de comenzar una guerra de edición entre `y $(partidarios. Personalmente, prefiero $(...)y hay algunas razones POSIX a favor de esa sintaxis ( pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) Pero no veo el punto de rechazar una respuesta correcta para algo que no está relacionado con el pregunta.
Sylvain Leroux
2
¿Podría alguien explicar por qué $( ... )se prefiere a los backticks en el contexto de esta respuesta?
user1717828
" ¿Qué error soluciona eso ?" No soluciona un error. El $(..)estilo se anida más fácilmente y AIUI es el enfoque preferido de POSIX. Ciertamente no tenía la intención de comenzar ninguna forma de guerra de edición, prefiriendo comentar con una sugerencia en lugar de cambiar su excelente respuesta.
roaima
@ user1717828 ver aquí y aquí y aquí para mayor claridad.
roaima
16

En Linux, para determinar si la entrada estándar se redirige /dev/null, puede verificar si /proc/self/fd/0tiene el mismo dispositivo e inodo que /dev/null:

if [ /proc/self/fd/0 -ef /dev/null ]; then echo yes; else echo no; fi

Puedes usar en /dev/stdinlugar de /proc/self/fd/0.

Si desea verificar si la entrada estándar se redirige desde el dispositivo nulo, debe comparar los números de dispositivo mayor y menor, por ejemplo utilizando stat(vea también la respuesta de mosvy ):

if [ "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" ]; then echo yes; else echo no; fi

o, si no te importa que esto sea específico de Linux ,

if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
Stephen Kitt
fuente
2
-ef, True si FILE1 y FILE2 se refieren al mismo dispositivo e inodo
Rui F Ribeiro
muy genial. bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'entonces haz eso otra vez con </dev/null. +1
glenn jackman
1
El hecho de /dev/stdinser un enlace simbólico al archivo original es específico de Linux, por lo que todas esas soluciones son específicas de Linux de todos modos. Los statbasados ​​en requieren GNU o busybox stat. Con versiones recientes de GNU stat, puede utilizar stat -para hacer un fstat()fd 0 que luego funcionaría en sistemas que no sean Linux.
Stéphane Chazelas
@ Stéphane la última parte es exactamente la situación en la que se encontró el OP, por eso escribí ambas soluciones, distinguiendo entre /dev/nully el dispositivo nulo.
Stephen Kitt el
Vaya, me perdí eso.
Stéphane Chazelas
3

Portablemente, para verificar que stdin es el nulldispositivo (abierto /dev/nullo no (como una copia de /dev/null)), con zsh(cuya statconstrucción es anterior a GNU y FreeBSD statpor cierto (aunque no IRIX ')):

zmodload zsh/stat
if [ "$(stat +rdev -f 0)" = "$(stat +rdev /dev/null)" ]; then
  echo stdin is open on the null device
fi

(tenga en cuenta que no dice si el descriptor de archivo estaba abierto en modo de solo lectura, solo escritura o lectura + escritura).

Para verificar que esté abierto /dev/nullespecíficamente en el archivo actual (no /some/chroot/dev/nullpor ejemplo), solo en Linux (donde /dev/stdinse implementa como un enlace simbólico al archivo abierto en fd 0 en lugar de un dispositivo especial que cuando se abre actúa como un dup(0)en otros sistemas):

if [ /dev/stdin -ef /dev/null ]; then
  echo stdin is open on /dev/null
fi

En no Linux, puedes probar:

if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
  echo stdin is open on /dev/null
fi
Stéphane Chazelas
fuente