Bagaimana cara memeriksa apakah stdin / dev / null dari shell?

9

Di Linux, apakah ada cara agar skrip shell memeriksa apakah input standarnya dialihkan dari perangkat nol (1, 3) * , idealnya tanpa membaca apa pun?

Perilaku yang diharapkan adalah:

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

EDIT

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

Saya kira saya "hanya" perlu membaca nomor maj / min dari perangkat input . Tetapi saya tidak dapat menemukan cara untuk melakukan itu dari shell.


* Tidak perlu adil /dev/null, tetapi perangkat null apa pun meskipun dibuat secara manual mknod.

Sylvain Leroux
sumber
2
Apakah Anda perlu tahu apakah itu /dev/null, atau hanya saja itu bukan tty?
roaima
Output { readlink -f /dev/stdin; } <&6untuk kasus di mana Anda menggunakan exec dan menghapus node /root/secretunknownname (deleted). Karena ini menunjukkan bahwa file terhapus: Apakah itu tidak cukup untuk apa yang Anda butuhkan?
Isaac
Saya perlu (sebenarnya "diperlukan") untuk mengetahui apakah input standar adalah perangkat null.
Sylvain Leroux
2
Saya mencoba untuk menemukan jalan saya dalam sistem industri (dirancang sangat buruk!). Dan terkadang memetakan input utilitas pekerja ke perangkat nol, atau, kadang-kadang, ke perangkat perangkat keras yang sebenarnya. Dalam kedua kasus menggunakan kode yang sama, tetapi dengan angka dev utama / minor yang berbeda. Kami mencoba melacak kapan (dan mengapa!) Kadang-kadang ia memilih untuk menggunakan satu atau yang lain. Untuk saat ini, statsolusinya adalah satu-satunya yang berfungsi.
Sylvain Leroux
"Jadi, contoh pada EDIT Anda tidak benar-benar mereproduksi masalah yang Anda uraikan, atau: bukan?" Itu benar. Input masuk diarahkan dari perangkat null . Yang biasanya diakses melalui /dev/null, tetapi tidak perlu. Anda dapat "alias" dengan mknodhuruf s yang diilustrasikan dalam contoh saya.
Sylvain Leroux

Jawaban:

18

Di linux, Anda dapat melakukannya dengan:

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

Di linux tanpa stat (1) (mis. Busybox di router Anda):

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

Di * bsd:

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

Pada sistem seperti * bsd dan solaris, /dev/stdin, /dev/fd/0dan /proc/PID/fd/0tidak "ajaib" symlink seperti pada linux, tetapi perangkat karakter yang akan beralih ke file nyata ketika dibuka . Stat (2) di path mereka akan mengembalikan sesuatu yang berbeda dari fstat (2) pada deskriptor file yang dibuka.

Ini berarti bahwa contoh linux tidak akan berfungsi di sana, bahkan dengan GNU coreutils diinstal. Jika versi stat GNU (1) cukup baru, Anda dapat menggunakan -argumen untuk membiarkannya melakukan fstat (2) pada deskriptor file 0, seperti stat (1) dari * bsd:

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

Ini juga sangat mudah untuk melakukan pemeriksaan portable dalam bahasa apa pun yang menawarkan antarmuka ke fstat (2), misalnya. di perl:

stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
mosvy
sumber
1
Karena jenis perangkatnya /dev/nulladalah 1:3, Anda dapat segera mengujinya.
RudiC
12
FWIW pertanyaannya bukan tentang sintaks shell. Untuk apa yang menyangkut masalah saya, jika jawabannya hanya mengarahkan saya ke statperintah saya sudah cukup puas. Saya tidak mengerti gunanya memulai perang penyuntingan antara `dan $(pendukung. Secara pribadi, saya lebih suka $(...)dan ada beberapa alasan POSIX yang mendukung sintaks itu ( pubs.opengroup.org/onlinepubs/9699919799/xrat/... ) Tapi saya tidak melihat gunanya menurunkan jawaban yang seharusnya benar untuk sesuatu yang tidak terkait dengan pertanyaan.
Sylvain Leroux
2
Bisakah seseorang menjelaskan mengapa $( ... )lebih disukai daripada backticks dalam konteks jawaban ini?
user1717828
" bug apa yang diperbaiki "? Itu tidak memperbaiki bug. The $(..)gaya sarang lebih mudah dan AIUI adalah pendekatan disukai POSIX. Saya tentu tidak bermaksud memulai segala bentuk perang penyuntingan, lebih memilih untuk berkomentar dengan saran daripada mengubah jawaban Anda yang luar biasa.
roaima
@ user1717828 lihat di sini dan di sini dan di sini untuk kejelasan.
roaima
16

Di Linux, untuk menentukan apakah input standar dialihkan /dev/null, Anda dapat memeriksa apakah /proc/self/fd/0memiliki perangkat dan inode yang sama dengan /dev/null:

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

Anda bisa menggunakan /dev/stdinbukan /proc/self/fd/0.

Jika Anda ingin memeriksa apakah input standar dialihkan dari perangkat nol, Anda perlu membandingkan nomor perangkat utama dan kecil, misalnya menggunakan stat(lihat juga jawaban mosvy ):

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

atau, jika Anda tidak peduli tentang hal ini khusus Linux ,

if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
Stephen Kitt
sumber
2
-Ef, Benar jika FILE1 dan FILE2 merujuk ke perangkat dan inode yang sama
Rui F Ribeiro
sangat keren. bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'lalu lakukan itu lagi dengan </dev/null. +1
glenn jackman
1
The /dev/stdinmenjadi symlink ke file asli khusus untuk Linux, sehingga semua orang solusi yang spesifik Linux pula. Yang statberbasis membutuhkan GNU atau busybox stat. Dengan versi GNU terbaru stat, Anda dapat menggunakan stat -untuk melakukan fstat()pada fd 0 yang kemudian akan bekerja pada sistem non-Linux.
Stéphane Chazelas
@ Stéphane bagian terakhir persis situasi OP berlari ke dalam, itulah sebabnya saya menulis kedua solusi, membedakan antara /dev/nulldan perangkat nol.
Stephen Kitt
Ups, ketinggalan itu.
Stéphane Chazelas
3

Portably, untuk memeriksa bahwa stdin adalah nullperangkat (terbuka /dev/nullatau tidak (seperti salinan /dev/null)), dengan zsh(yang statbawaannya mendahului baik GNU dan FreeBSD statdengan cara (bukan IRIX 'meskipun))):

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

(perhatikan bahwa tidak disebutkan apakah deskriptor file terbuka dalam mode baca-saja, tulis-saja atau baca + tulis).

Untuk memeriksa apakah itu terbuka pada /dev/nullfile saat ini secara khusus (bukan /some/chroot/dev/nullmisalnya), hanya di Linux (di mana /dev/stdindiimplementasikan sebagai symlink ke file yang terbuka di fd 0 alih-alih perangkat khusus yang ketika terbuka bertindak seperti dup(0)di sistem lain):

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

Di non-Linux, Anda dapat mencoba:

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