Wie überprüfe ich, ob stdin / dev / null aus der Shell ist?

8

Gibt es unter Linux eine Möglichkeit für ein Shell-Skript zu überprüfen, ob seine Standardeingabe vom Nullgerät (1, 3) * umgeleitet wird , idealerweise ohne etwas zu lesen?

Das erwartete Verhalten wäre:

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

EDIT

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

Ich vermute , dass ich „nur“ Notwendigkeit , die maj / min Anzahl der Eingabe lesen Geräts . Aber ich kann keinen Weg finden, dies aus der Shell heraus zu tun.


* Nicht nur notwendig /dev/null, sondern jedes Nullgerät, auch wenn es manuell mit erstellt wurde mknod.

Sylvain Leroux
quelle
2
Müssen Sie wissen, ob es ist /dev/nulloder nur, dass es kein tty ist?
Roaima
Die Ausgabe von { readlink -f /dev/stdin; } <&6für den Fall, in dem Sie exec verwendet und den Knoten entfernt haben, ist /root/secretunknownname (deleted). Wie es zeigt, wurde die Datei gelöscht: Reicht das nicht für das, was Sie brauchen?
Isaac
Ich muss (eigentlich "benötigt") wissen, ob die Standardeingabe das Nullgerät war.
Sylvain Leroux
2
Ich versuche mich in einem (sehr schlecht gestalteten!) Industriesystem zurechtzufinden. Manchmal wird die Eingabe des Worker-Dienstprogramms dem Nullgerät oder manchmal dem tatsächlichen Hardwaregerät zugeordnet. In beiden Fällen wird derselbe Code verwendet, jedoch mit unterschiedlichen Haupt- / Nebenentwicklungsnummern. Wir versuchen zu verfolgen, wann (und warum!) Manchmal das eine oder andere verwendet wurde. Derzeit statfunktioniert nur die Lösung.
Sylvain Leroux
"Das Beispiel in Ihrer EDIT gibt das von Ihnen beschriebene Problem also nicht wirklich wieder, oder: oder?" Es tut. Eingang in von der Null weitergeleitet Gerät . Was normalerweise über zugegriffen wird /dev/null, aber nicht notwendig. Sie können "Alias" mit mknods in meinem Beispiel dargestellt.
Sylvain Leroux

Antworten:

18

Unter Linux können Sie dies tun mit:

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

Unter Linux ohne stat (1) (z. B. die Busybox auf Ihrem Router):

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

On * bsd:

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

Auf Systemen wie * bsd und solaris, /dev/stdin, /dev/fd/0und /proc/PID/fd/0sind nicht „magische“ Symlinks als auf Linux, aber Zeichengeräte , die auf die reale Datei wechseln wird , wenn sie geöffnet . Ein stat (2) auf ihrem Pfad gibt etwas anderes zurück als ein fstat (2) im geöffneten Dateideskriptor.

Dies bedeutet, dass das Linux-Beispiel dort nicht funktioniert, selbst wenn GNU-Coreutils installiert sind. Wenn die Versionen von GNU stat (1) aktuell genug sind, können Sie das -Argument verwenden, um ein fstat (2) für den Dateideskriptor 0 auszuführen, genau wie stat (1) von * bsd:

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

Es ist auch sehr einfach, die Prüfung portabel in jeder Sprache durchzuführen, die eine Schnittstelle zu fstat (2) bietet, z. in perl:

stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
Mosvy
quelle
1
Da der Gerätetyp /dev/nullist 1:3, können Sie dies sofort testen.
RudiC
12
FWIW war die Frage nicht über die Shell-Syntax. Was mein Problem betrifft, statwäre ich schon ziemlich zufrieden gewesen , wenn die Antwort mich nur auf den Befehl hingewiesen hätte. Ich sehe keinen Sinn darin, einen Schnittkrieg zwischen `und $(Unterstützern zu beginnen. Persönlich bevorzuge ich $(...)und es gibt einige POSIX- Gründe für diese Syntax ( pubs.opengroup.org/onlinepubs/9699919799/xrat/… ). Aber ich sehe keinen Sinn darin, eine ansonsten korrekte Antwort für etwas herunterzustimmen, das nicht mit dem zusammenhängt Frage.
Sylvain Leroux
2
Könnte jemand erklären, warum $( ... )im Zusammenhang mit dieser Antwort Backticks vorgezogen werden?
user1717828
" Welchen Fehler behebt das ?" Es behebt keinen Fehler. Der $(..)Stil verschachtelt leichter und AIUI ist der bevorzugte Ansatz von POSIX. Ich hatte sicherlich nicht vorgehabt, irgendeine Form von Bearbeitungskrieg zu beginnen, sondern lieber mit einem Vorschlag zu kommentieren, als Ihre ausgezeichnete Antwort zu ändern.
Roaima
@ user1717828 siehe hier und hier und hier für etwas Klarheit.
Roaima
15

Um festzustellen, ob unter Linux die Standardeingabe umgeleitet wird /dev/null, können Sie überprüfen, ob /proc/self/fd/0dasselbe Gerät und derselbe Inode vorhanden sind wie /dev/null:

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

Sie können /dev/stdinanstelle von verwenden /proc/self/fd/0.

Wenn Sie überprüfen möchten, ob die Standardeingabe vom Nullgerät umgeleitet wird, müssen Sie die Haupt- und Nebengerätenummern vergleichen, z. B. mit stat(siehe auch die Antwort von mosvy ):

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

oder, wenn Sie sich nicht dafür interessieren, dass dies Linux-spezifisch ist ,

if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
Stephen Kitt
quelle
2
-ef, True, wenn sich FILE1 und FILE2 auf dasselbe Gerät und denselben Inode beziehen
Rui F Ribeiro
sehr cool. bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'dann mach das nochmal mit </dev/null. +1
Glenn Jackman
1
Der /dev/stdinSymlink zur Originaldatei ist spezifisch für Linux, daher sind alle diese Lösungen sowieso Linux-spezifisch. Die statbasierten erfordern GNU oder Busybox stat. Mit neueren Versionen von GNU statkönnen Sie stat -ein fstat()on fd 0 ausführen, das dann auf Nicht-Linux-Systemen funktioniert.
Stéphane Chazelas
@ Stéphane Der letzte Teil ist genau die Situation, in die das OP geraten ist, weshalb ich beide Lösungen geschrieben habe, wobei zwischen /dev/nullund dem Null-Gerät unterschieden wurde.
Stephen Kitt
Hoppla, das habe ich verpasst.
Stéphane Chazelas
3

Um zu überprüfen, ob stdin das nullGerät ist (geöffnet auf /dev/nulloder nicht (wie eine Kopie von /dev/null)), mit zsh(dessen stateingebautes Gerät übrigens vor GNU und FreeBSD liegt stat(allerdings nicht vor IRIX)):

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

(Beachten Sie, dass nicht angegeben wird, ob der Dateideskriptor im Nur-Lese-, Nur-Schreib- oder Lese- / Schreibmodus geöffnet war.)

Um zu überprüfen, ob es in der aktuellen /dev/nullDatei speziell (nicht /some/chroot/dev/nullzum Beispiel) geöffnet ist , nur unter Linux (wobei /dev/stdines als Symlink zu der auf fd 0 geöffneten Datei anstelle eines speziellen Geräts implementiert ist, das sich beim Öffnen wie dup(0)in anderen Systemen verhält):

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

Unter Nicht-Linux können Sie Folgendes versuchen:

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