Как проверить, является ли stdin / dev / null из оболочки?

9

В Linux есть ли способ для сценария оболочки проверить, перенаправлен ли его стандартный ввод с нулевого устройства (1, 3) * , в идеале, ничего не читая?

Ожидаемое поведение будет:

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

EDIT

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

Я подозреваю, что мне «просто» нужно прочитать число маж / мин устройства ввода . Но я не могу найти способ сделать это из оболочки.


* Нет необходимости просто /dev/null, но любое нулевое устройство, даже если оно создано вручную с помощью mknod.

Сильвен Леру
источник
2
Вам нужно знать, если это /dev/null, или просто это не tty?
Ройма
Выход { readlink -f /dev/stdin; } <&6для случая , когда вы использовали Exec и удалить узел /root/secretunknownname (deleted). Как показывает, что файл был удален: Разве этого недостаточно для того, что вам нужно?
Исаак
Мне нужно (на самом деле «необходимо»), чтобы знать, был ли стандартный ввод нулевым устройством.
Сильвен Леру
2
Я пытаюсь найти свой путь в (очень плохо спроектированной!) Промышленной системе. И иногда он отображает ввод рабочей утилиты на нулевое устройство или, иногда, на фактическое аппаратное устройство. В обоих случаях используется один и тот же код, но с разными старшими / младшими номерами разработчиков. Мы пытаемся отследить, когда (и почему!) Он иногда выбирал один или другой. На данный момент statрешение работает только одно.
Сильвен Леру
«Итак, пример вашего EDIT не воспроизводит проблему, которую вы описываете, или: да?» Оно делает. Ввод перенаправлен с нулевого устройства . К которому обычно обращаются через /dev/null, но не обязательно. Вы можете "псевдоним" с mknods, иллюстрируется в моем примере.
Сильвен Леру

Ответы:

18

На Linux вы можете сделать это с:

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

В Linux без stat (1) (например, busybox на вашем маршрутизаторе):

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

На * BSD:

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

В системах , такие как * BSD и Solaris, /dev/stdin, /dev/fd/0и /proc/PID/fd/0не являются «магическими» символьными ссылками как на Linux, но символьные устройства , которые будут переключаться на реальный файл при открытии . Stat (2) на их пути вернет что-то отличное от fstat (2) в дескрипторе открытого файла.

Это означает, что пример Linux не будет работать там, даже с установленным GNU coreutils. Если версии GNU stat (1) достаточно недавние, вы можете использовать -аргумент, чтобы позволить ему выполнить fstat (2) для файлового дескриптора 0, так же как stat (1) из * bsd:

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

Также очень легко выполнить проверку переносимо на любом языке, который предлагает интерфейс для fstat (2), например. в perl:

stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
mosvy
источник
1
Поскольку тип устройства /dev/nullесть 1:3, вы можете проверить это немедленно.
RudiC
12
FWIW вопрос был не о синтаксисе оболочки. Что касается моей проблемы, если бы ответ только указал мне на statкоманду, я был бы уже вполне удовлетворен. Я не вижу смысла в открытии войны редактирования между `и $(сторонников. Лично я предпочитаю, $(...)и есть некоторые обоснования POSIX в пользу этого синтаксиса ( pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) Но я не вижу смысла в даунтинге, если нет правильного ответа на что-то, не связанное с вопрос.
Сильвен Леру
2
Может ли кто-нибудь объяснить, почему $( ... )в контексте этого ответа предпочтительнее обратных кавычек?
user1717828
" какую ошибку это исправляет "? Это не исправляет ошибку. В $(..)гнездах стиля легко и AIUI является POSIX предпочтительного подхода. Я, конечно, не собирался начинать редактирование войны, предпочитая комментировать с предложением, а не менять свой отличный ответ.
Ройма
@ user1717828 смотрите здесь и здесь и здесь для некоторой ясности.
Ройма
16

В Linux, чтобы определить, перенаправлен ли стандартный ввод /dev/null, вы можете проверить, /proc/self/fd/0имеет ли то же устройство и inode, что и /dev/null:

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

Вы можете использовать /dev/stdinвместо /proc/self/fd/0.

Если вы хотите проверить, перенаправлен ли стандартный ввод с нулевого устройства, вам нужно сравнить старшие и младшие номера устройств, например, используя stat(см. Также ответ mosvy ):

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

или, если вас не волнует, что это специфично для Linux ,

if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
Стивен Китт
источник
2
-ef, True, если FILE1 и FILE2 относятся к одному и тому же устройству и индоду
Rui F Ribeiro
очень круто. bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'затем сделайте это снова с </dev/null. +1
Гленн Джекман
1
/dev/stdinБыть символическим в исходный файл специфичен для Linux, так что все эти решения являются Linux-специфические в любом случае. Для statоснованных требуется GNU или busybox stat. С недавних версиях GNU stat, вы можете использовать , stat -чтобы сделать fstat()на FD 0 , который будет затем работать на системах , отличных от Linux.
Стефан Шазелас
@ Стефан, последняя часть - это именно та ситуация, с которой столкнулся ОП, поэтому я написал оба решения, различая /dev/nullнулевое устройство и устройство.
Стивен Китт
Упс, пропустил это.
Стефан Шазелас
3

Для проверки того, что stdin - это nullустройство (открыто /dev/nullили нет (как копия /dev/null)), с zsh(чья statвстроенная версия предшествует GNU и FreeBSD stat, кстати (не IRIX ', хотя))):

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

(обратите внимание, что в нем не указано, был ли дескриптор файла открыт в режиме «только чтение», «только запись» или «чтение + запись»).

Чтобы проверить, что он открыт /dev/nullконкретно /some/chroot/dev/nullдля текущего файла (не, например), только в Linux (где /dev/stdinон реализован в виде символической ссылки на файл, открытый на fd 0 вместо специального устройства, которое при открытии действует как a dup(0)в других системах):

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

На не Linux вы можете попробовать:

if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
  echo stdin is open on /dev/null
fi
Стефан Шазелас
источник