Como verificar se stdin é / dev / null a partir do shell?

9

No Linux, existe uma maneira de um script de shell verificar se sua entrada padrão é redirecionada do dispositivo nulo (1, 3) * , idealmente sem ler nada?

O comportamento esperado seria:

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

EDIT

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

Eu suspeito que "apenas" preciso ler o número principal / min do dispositivo de entrada . Mas não consigo encontrar uma maneira de fazer isso a partir do shell.


* Não é necessário apenas /dev/null, mas qualquer dispositivo nulo, mesmo se criado manualmente com mknod.

Sylvain Leroux
fonte
2
Você precisa saber se é /dev/nullou apenas não é um tty?
roaima
A saída de { readlink -f /dev/stdin; } <&6para o caso em que você usou exec e removeu o nó é /root/secretunknownname (deleted). Como mostra que o arquivo foi excluído: não é suficiente para o que você precisa?
Isaac
Eu preciso (realmente "necessário") para saber se a entrada padrão foi o dispositivo nulo.
22418 Sylvain Leroux
2
Estou tentando encontrar meu caminho em um sistema industrial (muito mal projetado!). E às vezes mapeia a entrada do utilitário de trabalho para o dispositivo nulo ou, outras vezes, para o dispositivo de hardware real. Nos dois casos, use o mesmo código, mas com diferentes números dev de maior / menor. Estamos tentando rastrear quando (e por que!) Algumas vezes ele optou por usar um ou outro. Por enquanto, a statsolução é a única que funciona.
Sylvain Leroux
"Então, o exemplo no seu EDIT não reproduz realmente o problema que você descreve, ou: faz?" Faz. Entrada redirecionada do dispositivo nulo . Geralmente acessado /dev/null, mas não necessário. Você pode "alias" é com mknods ilustrado no meu exemplo.
22418 Sylvain Leroux

Respostas:

18

No linux, você pode fazer isso com:

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

Em um Linux sem stat (1) (por exemplo, o busybox no seu roteador):

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

Em * bsd:

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

Em sistemas como o * BSD e Solaris, /dev/stdin, /dev/fd/0e /proc/PID/fd/0não são "mágicas" links simbólicos como em Linux, mas dispositivos de caracteres que irá mudar para o arquivo verdadeiro quando aberto . Um stat (2) no caminho retornará algo diferente de um fstat (2) no descritor de arquivo aberto.

Isso significa que o exemplo do linux não funcionará lá, mesmo com o GNU coreutils instalado. Se as versões do GNU stat (1) forem recentes o suficiente, você poderá usar o -argumento para fazer um fstat (2) no descritor de arquivo 0, assim como o stat (1) de * bsd:

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

É também muito fácil de fazer o check portably em qualquer idioma que oferece uma interface para fstat (2), por exemplo. em perl:

stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
mosvy
fonte
1
Como o tipo de dispositivo /dev/nullé 1:3, você pode testar para isso imediatamente.
26418 RudiC
12
FWIW, a pergunta não era sobre sintaxe de shell. No que diz respeito ao meu problema, se a resposta tivesse apenas me apontado para o statcomando, eu já estaria bastante satisfeito. Não vejo sentido em iniciar uma guerra de edição entre `e $(apoiadores. Pessoalmente, prefiro $(...)e existem algumas justificativas do POSIX em favor dessa sintaxe ( pubs.opengroup.org/onlinepubs/9699919799/xrat/… ). questão.
22418 Sylvain Leroux
2
Alguém poderia explicar por que $( ... )é preferível a backticks no contexto desta resposta?
user1717828
" que bug corrige "? Não corrige um bug. O $(..)estilo é mais fácil e o AIUI é a abordagem preferida do POSIX. Eu certamente não pretendia iniciar nenhuma forma de guerra de edição, preferindo comentar com uma sugestão do que alterar sua excelente resposta.
roaima 27/11
@ user1717828 veja aqui e aqui e aqui para ter mais clareza.
roaima
16

No Linux, para determinar se a entrada padrão é redirecionada /dev/null, você pode verificar se /proc/self/fd/0possui o mesmo dispositivo e inode que /dev/null:

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

Você pode usar em /dev/stdinvez de /proc/self/fd/0.

Se você deseja verificar se a entrada padrão é redirecionada do dispositivo nulo, é necessário comparar os números principais e secundários do dispositivo, por exemplo, usando stat(consulte também a resposta de mosvy ):

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

ou, se você não se importa que isso seja específico do Linux ,

if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
Stephen Kitt
fonte
2
-ef, True se FILE1 e FILE2 se referem ao mesmo dispositivo e inode
Rui F Ribeiro
muito legal. bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'então faça isso novamente com </dev/null. 1)
glenn jackman
1
O fato de /dev/stdinser um link simbólico para o arquivo original é específico do Linux; portanto, todas essas soluções são específicas do Linux. Os statbaseados requerem GNU ou busybox stat. Com versões recentes do GNU stat, você pode usar stat -para fazer um fstat()em fd 0, que passaria a trabalhar em sistemas não-Linux.
Stéphane Chazelas
@ Stéphane a última parte é exatamente a situação em que o OP se encontrou, e foi por isso que escrevi as duas soluções, distinguindo entre /dev/nulle o dispositivo nulo.
Stephen Kitt
Opa, senti falta disso.
Stéphane Chazelas
3

Portably, para verificar se stdin é o nulldispositivo (aberto /dev/nullou não (como uma cópia de /dev/null)), with zsh(cujo statbuilt-in é anterior ao GNU e ao FreeBSD stata propósito (embora não seja o IRIX))):

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

(observe que ele não diz se o descritor de arquivo foi aberto no modo somente leitura, somente gravação ou modo leitura + gravação).

Para verificar se ele está aberto /dev/nullespecificamente no arquivo atual (não /some/chroot/dev/nullpor exemplo), apenas no Linux (onde /dev/stdiné implementado como um link simbólico para o arquivo aberto no fd 0, em vez de um dispositivo especial que, quando aberto, atua como um dup(0)em outros sistemas):

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

No não Linux, você pode tentar:

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