Làm thế nào để kiểm tra xem stdin có / dev / null từ shell không?


9

Trên Linux, có cách nào để tập lệnh shell kiểm tra xem đầu vào tiêu chuẩn của nó có được chuyển hướng từ thiết bị null không (1, 3) * , lý tưởng mà không cần đọc gì?

Hành vi dự kiến ​​sẽ là:

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

EDIT

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

Tôi nghi ngờ tôi "chỉ" cần đọc số lượng lớn / phút của thiết bị đầu vào . Nhưng tôi không thể tìm ra cách làm điều đó từ cái vỏ.


* Không cần thiết chỉ /dev/null, nhưng bất kỳ thiết bị null nào ngay cả khi được tạo thủ công bằng mknod.


2
Bạn có cần biết nếu nó không /dev/null, hoặc chỉ là nó không phải là một tty?
roaima

Đầu ra của { readlink -f /dev/stdin; } <&6trường hợp bạn đã sử dụng exec và loại bỏ nút là /root/secretunknownname (deleted). Vì nó cho thấy rằng tập tin đã bị xóa: Không đủ cho những gì bạn cần?
Isaac

Tôi cần (thực sự là "cần thiết") để biết liệu đầu vào tiêu chuẩn có phải là thiết bị không.
Sylvain Leroux

2
Tôi đang cố gắng tìm đường trong một hệ thống công nghiệp (được thiết kế rất kém!). Và đôi khi nó ánh xạ đầu vào của tiện ích worker vào thiết bị null, hoặc, đôi khi, vào thiết bị phần cứng thực tế. Trong cả hai trường hợp sử dụng cùng một mã, nhưng với các số dev chính / phụ khác nhau. Chúng tôi đang cố gắng theo dõi khi nào (và tại sao!) Đôi khi nó chọn sử dụng cái này hay cái khác. Cho đến nay, statgiải pháp là người duy nhất làm việc.
Sylvain Leroux

"Vì vậy, ví dụ về EDIT của bạn không thực sự tái tạo vấn đề mà bạn mô tả, hoặc: có phải không?" Nó làm. Đầu vào được chuyển hướng từ thiết bị null . Mà thường được truy cập thông qua /dev/null, nhưng không cần thiết. Bạn có thể "bí danh" với mknods được minh họa trong ví dụ của tôi.
Sylvain Leroux

Câu trả lời:


18

Trên linux, bạn có thể làm điều đó với:

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

Trên linux không có stat (1) (ví dụ: busybox trên bộ định tuyến của bạn):

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

Trên * bsd:

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

Trên các hệ thống như * bsd và solaris, /dev/stdin, /dev/fd/0/proc/PID/fd/0không phải là "ma thuật" liên kết tượng trưng như trên Linux, nhưng các thiết bị nhân vật đó sẽ chuyển sang các tập tin thực khi mở ra . Một stat (2) trên đường dẫn của chúng sẽ trả về một cái gì đó khác với fstat (2) trên bộ mô tả tệp đã mở.

Điều này có nghĩa là ví dụ linux sẽ không hoạt động ở đó, ngay cả khi cài đặt lõi GNU. Nếu các phiên bản của GNU stat (1) đủ gần đây, bạn có thể sử dụng -đối số để cho phép nó thực hiện fstat (2) trên bộ mô tả tệp 0, giống như stat (1) từ * bsd:

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

Nó cũng rất dễ dàng để làm việc kiểm tra portably trong bất kỳ ngôn ngữ trong đó cung cấp một giao diện để fstat (2), ví dụ. trong perl:

stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }

1
Là loại thiết bị /dev/null1:3, bạn có thể kiểm tra ngay lập tức.
RudiC

12
FWIW câu hỏi không phải là về cú pháp shell. Đối với những gì liên quan đến vấn đề của tôi, nếu câu trả lời chỉ cho tôi biết statlệnh thì tôi đã khá hài lòng. Tôi không thấy điểm bắt đầu cuộc chiến chỉnh sửa giữa `$(những người ủng hộ. Cá nhân, tôi thích $(...)và có một số lý do POSIX ủng hộ cú pháp đó ( pubs.opengroup.org/onlinepub/9699919799/xrat/iêu ) Nhưng tôi không thấy điểm nào trong việc bỏ qua một câu trả lời đúng cho những điều không liên quan đến câu hỏi
Sylvain Leroux

2
Ai đó có thể giải thích tại sao $( ... )được ưa thích hơn backticks trong bối cảnh của câu trả lời này?
dùng1717828

" lỗi gì mà sửa "? Nó không sửa một lỗi. Các $(..)tổ phong cách dễ dàng hơn và AIUI là phương pháp ưa thích POSIX. Tôi chắc chắn đã không có ý định bắt đầu bất kỳ hình thức chiến tranh chỉnh sửa nào, thích bình luận bằng một đề nghị hơn là thay đổi câu trả lời xuất sắc của bạn.
roaima

@ user1717828 xem tại đâyđâyđây để hiểu rõ hơn.
roaima

16

Trên Linux, để xác định xem đầu vào tiêu chuẩn có được chuyển hướng từ hay không /dev/null, bạn có thể kiểm tra xem /proc/self/fd/0có cùng thiết bị và inode như /dev/null:

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

Bạn có thể sử dụng /dev/stdinthay vì /proc/self/fd/0.

Nếu bạn muốn kiểm tra xem đầu vào tiêu chuẩn có được chuyển hướng từ thiết bị null hay không, bạn cần so sánh số thiết bị chính và phụ, ví dụ như sử dụng stat(xem thêm câu trả lời của mosvy ):

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

hoặc, nếu bạn không quan tâm đến việc này là dành riêng cho Linux ,

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

2
-ef, Đúng nếu FILE1 và FILE2 đề cập đến cùng một thiết bị và inode
Rui F Ribeiro

rất tuyệt. bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'sau đó làm lại với </dev/null. +1
glenn jackman

1
Việc /dev/stdinlà một liên kết tượng trưng cho tệp gốc là dành riêng cho Linux, vì vậy tất cả các giải pháp đó đều dành riêng cho Linux. Những statcái dựa trên yêu cầu GNU hoặc busybox stat. Với các phiên bản gần đây của GNU stat, bạn có thể sử dụng stat -để thực hiện fstat()trên fd 0, sau đó sẽ hoạt động trên các hệ thống không phải là Linux.
Stéphane Chazelas

@ Stéphane phần cuối cùng chính xác là tình huống mà OP gặp phải, đó là lý do tại sao tôi viết ra cả hai giải pháp, phân biệt giữa /dev/nullvà thiết bị null.
Stephen Kitt

Rất tiếc, đã bỏ lỡ điều đó.
Stéphane Chazelas

3

Có thể kiểm tra xem stdin có phải là nullthiết bị (mở /dev/nullhay không (giống như bản sao /dev/null)), với zsh(có phần statdựng sẵn trước cả GNU và FreeBSD stat(không phải IRIX ')):

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

(lưu ý rằng nó không cho biết nếu bộ mô tả tệp được mở ở chế độ chỉ đọc, chỉ ghi hoặc đọc + ghi).

Để kiểm tra xem nó có mở trên /dev/nulltệp hiện tại một cách cụ thể (không phải /some/chroot/dev/nullví dụ), chỉ trên Linux ( /dev/stdinđược triển khai dưới dạng liên kết tượng trưng với tệp mở trên fd 0 thay vì một thiết bị đặc biệt khi mở hoạt động như dup(0)trong các hệ thống khác):

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

Trên phi Linux, bạn có thể thử:

if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
  echo stdin is open on /dev/null
fi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.