How to check if stdin is /dev/null from the shell?
up vote
6
down vote
favorite
On Linux, is there a way for a shell script to check if its standard input is redirected from the null device (1, 3) *, ideally without reading anything?
The expected behavior would be:
./checkstdinnull
-> no
./checkstdinnull < /dev/null
-> yes
echo -n | ./checkstdinnull
-> no
EDIT
mknod secretunknownname c 1 3
exec 6<secretunknownname
rm secretunknownname
./checkstdinnull <&6
-> yes
I suspect I "just" need to read the maj/min number of the input device. But I can't find a way of doing that from the shell.
*No necessary just
/dev/null
, but any null device even if manually created with mknod
.linux shell devices stdin
|
show 6 more comments
up vote
6
down vote
favorite
On Linux, is there a way for a shell script to check if its standard input is redirected from the null device (1, 3) *, ideally without reading anything?
The expected behavior would be:
./checkstdinnull
-> no
./checkstdinnull < /dev/null
-> yes
echo -n | ./checkstdinnull
-> no
EDIT
mknod secretunknownname c 1 3
exec 6<secretunknownname
rm secretunknownname
./checkstdinnull <&6
-> yes
I suspect I "just" need to read the maj/min number of the input device. But I can't find a way of doing that from the shell.
*No necessary just
/dev/null
, but any null device even if manually created with mknod
.linux shell devices stdin
2
Do you need to know if it's/dev/null
, or just that it's not a tty?
– roaima
13 hours ago
The output of{ readlink -f /dev/stdin; } <&6
for the case where you used exec and removed the node is/root/secretunknownname (deleted)
. As it shows that the file got deleted: Isn't that enough for what you need?
– Isaac
11 hours ago
I need (actually "needed") to know if the standard input was the null device.
– Sylvain Leroux
11 hours ago
But checking for the/dev/null
won't work forrm secretunknownname
, would it?
– Isaac
11 hours ago
I am trying to find my way in a (very poorly designed!) industrial system. And it sometimes maps input of worker utility to the null device, or, some other times, to the actual hardware device. In both cases using the same code, but with different major/minor dev numbers. We are trying to track when (and why!) it sometimes chose to use one or the other. For now, thestat
solution is the only one working.
– Sylvain Leroux
11 hours ago
|
show 6 more comments
up vote
6
down vote
favorite
up vote
6
down vote
favorite
On Linux, is there a way for a shell script to check if its standard input is redirected from the null device (1, 3) *, ideally without reading anything?
The expected behavior would be:
./checkstdinnull
-> no
./checkstdinnull < /dev/null
-> yes
echo -n | ./checkstdinnull
-> no
EDIT
mknod secretunknownname c 1 3
exec 6<secretunknownname
rm secretunknownname
./checkstdinnull <&6
-> yes
I suspect I "just" need to read the maj/min number of the input device. But I can't find a way of doing that from the shell.
*No necessary just
/dev/null
, but any null device even if manually created with mknod
.linux shell devices stdin
On Linux, is there a way for a shell script to check if its standard input is redirected from the null device (1, 3) *, ideally without reading anything?
The expected behavior would be:
./checkstdinnull
-> no
./checkstdinnull < /dev/null
-> yes
echo -n | ./checkstdinnull
-> no
EDIT
mknod secretunknownname c 1 3
exec 6<secretunknownname
rm secretunknownname
./checkstdinnull <&6
-> yes
I suspect I "just" need to read the maj/min number of the input device. But I can't find a way of doing that from the shell.
*No necessary just
/dev/null
, but any null device even if manually created with mknod
.linux shell devices stdin
linux shell devices stdin
edited 12 hours ago
asked 13 hours ago


Sylvain Leroux
40729
40729
2
Do you need to know if it's/dev/null
, or just that it's not a tty?
– roaima
13 hours ago
The output of{ readlink -f /dev/stdin; } <&6
for the case where you used exec and removed the node is/root/secretunknownname (deleted)
. As it shows that the file got deleted: Isn't that enough for what you need?
– Isaac
11 hours ago
I need (actually "needed") to know if the standard input was the null device.
– Sylvain Leroux
11 hours ago
But checking for the/dev/null
won't work forrm secretunknownname
, would it?
– Isaac
11 hours ago
I am trying to find my way in a (very poorly designed!) industrial system. And it sometimes maps input of worker utility to the null device, or, some other times, to the actual hardware device. In both cases using the same code, but with different major/minor dev numbers. We are trying to track when (and why!) it sometimes chose to use one or the other. For now, thestat
solution is the only one working.
– Sylvain Leroux
11 hours ago
|
show 6 more comments
2
Do you need to know if it's/dev/null
, or just that it's not a tty?
– roaima
13 hours ago
The output of{ readlink -f /dev/stdin; } <&6
for the case where you used exec and removed the node is/root/secretunknownname (deleted)
. As it shows that the file got deleted: Isn't that enough for what you need?
– Isaac
11 hours ago
I need (actually "needed") to know if the standard input was the null device.
– Sylvain Leroux
11 hours ago
But checking for the/dev/null
won't work forrm secretunknownname
, would it?
– Isaac
11 hours ago
I am trying to find my way in a (very poorly designed!) industrial system. And it sometimes maps input of worker utility to the null device, or, some other times, to the actual hardware device. In both cases using the same code, but with different major/minor dev numbers. We are trying to track when (and why!) it sometimes chose to use one or the other. For now, thestat
solution is the only one working.
– Sylvain Leroux
11 hours ago
2
2
Do you need to know if it's
/dev/null
, or just that it's not a tty?– roaima
13 hours ago
Do you need to know if it's
/dev/null
, or just that it's not a tty?– roaima
13 hours ago
The output of
{ readlink -f /dev/stdin; } <&6
for the case where you used exec and removed the node is /root/secretunknownname (deleted)
. As it shows that the file got deleted: Isn't that enough for what you need?– Isaac
11 hours ago
The output of
{ readlink -f /dev/stdin; } <&6
for the case where you used exec and removed the node is /root/secretunknownname (deleted)
. As it shows that the file got deleted: Isn't that enough for what you need?– Isaac
11 hours ago
I need (actually "needed") to know if the standard input was the null device.
– Sylvain Leroux
11 hours ago
I need (actually "needed") to know if the standard input was the null device.
– Sylvain Leroux
11 hours ago
But checking for the
/dev/null
won't work for rm secretunknownname
, would it?– Isaac
11 hours ago
But checking for the
/dev/null
won't work for rm secretunknownname
, would it?– Isaac
11 hours ago
I am trying to find my way in a (very poorly designed!) industrial system. And it sometimes maps input of worker utility to the null device, or, some other times, to the actual hardware device. In both cases using the same code, but with different major/minor dev numbers. We are trying to track when (and why!) it sometimes chose to use one or the other. For now, the
stat
solution is the only one working.– Sylvain Leroux
11 hours ago
I am trying to find my way in a (very poorly designed!) industrial system. And it sometimes maps input of worker utility to the null device, or, some other times, to the actual hardware device. In both cases using the same code, but with different major/minor dev numbers. We are trying to track when (and why!) it sometimes chose to use one or the other. For now, the
stat
solution is the only one working.– Sylvain Leroux
11 hours ago
|
show 6 more comments
4 Answers
4
active
oldest
votes
up vote
14
down vote
accepted
On linux, you can do it with:
stdin_is_dev_null(){ test "`stat -Lc %t:%T /dev/stdin`" = "`stat -Lc %t:%T /dev/null`"; }
On a linux without stat(1) (eg. the busybox on your 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`"; }
On systems like *bsd and solaris, /dev/stdin
, /dev/fd/0
and /proc/PID/fd/0
are not "magical" symlinks as on linux, but character devices which will switch to the real file when opened. A stat(2) on their path will return something different than a fstat(2) on the opened file descriptor.
This means that the linux example will not work there, even with GNU coreutils installed. If the versions of GNU stat(1) is recent enough, you can use the -
argument to let it do a fstat(2) on the file descriptor 0, just like the stat(1) from *bsd:
stdin_is_dev_null(){ test "`stat -Lc %t:%T -`" = "`stat -Lc %t:%T /dev/null`"; }
It's also very easy to do the check portably in any language which offers an interface to fstat(2), eg. in perl
:
stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
13
Would strongly recommend$( ...)
instead of backticks.
– roaima
13 hours ago
8
Not suggesting another command expansion. Simply that you replace the backtick characters with$(
and)
.stdin_is_dev_null(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)"; }
.
– roaima
12 hours ago
3
@roaima, I was just wondering why you're strongly recommend$(...)
instead of backticks? I use backticks most of the times and it works fine except in case of nested of backticks where I prefer$(...)
?
– finn
12 hours ago
3
@roaima what bug does that fix? my point was that I know & agree that$(...)
is much more robust wrt embedded quotings and also can be easily nested, but in this case that's not necessary. So let's not increase the entropy of the universe with unnecessary changes ;-) Better think of some way to eliminate the repetition; something likestat -c %t:%T /dev/stdin /dev/null | uniq -u
, but lighter.
– mosvy
12 hours ago
8
FWIW the question was not about shell syntax. For what is concerning my issue, if the answer had only pointed me to thestat
command I would have already been quite satisfied. I don't see the point in starting an editing war between`
and$(
supporters. Personally, I prefer$(...)
and there are some POSIX rationales in favor of that syntax (pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) But I don't see the point in downvoting an otherwise correct answer for something not related with the question.
– Sylvain Leroux
11 hours ago
|
show 5 more comments
up vote
14
down vote
On Linux, to determine whether standard input is redirected from /dev/null
, you can check whether /proc/self/fd/0
has the same device and inode as /dev/null
:
if [ /proc/self/fd/0 -ef /dev/null ]; then echo yes; else echo no; fi
You can use /dev/stdin
instead of /proc/self/fd/0
.
If you want to check whether standard input is redirected from the null device, you need to compare major and minor device numbers, for example using stat
(see also mosvy’s answer):
if [ "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" ]; then echo yes; else echo no; fi
or, if you don’t care about this being Linux-specific,
if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
1
-ef, True if FILE1 and FILE2 refer to the same device and inode
– Rui F Ribeiro
13 hours ago
very cool.bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'
then do that again with</dev/null
. +1
– glenn jackman
11 hours ago
1
The/dev/stdin
being a symlink to the original file is specific to Linux, so all those solutions are Linux-specific anyway. Thestat
-based ones require GNU or busyboxstat
. With recent versions of GNUstat
, you can usestat -
to do afstat()
on fd 0 which would then work on non-Linux systems.
– Stéphane Chazelas
9 hours ago
@Stéphane the last part is exactly the situation the OP ran into, which is why I wrote up both solutions, distinguishing between/dev/null
and the null device.
– Stephen Kitt
9 hours ago
Oops, missed that.
– Stéphane Chazelas
7 hours ago
add a comment |
up vote
1
down vote
Portably, to check that stdin is the null
device (open on /dev/null
or not (like a copy of /dev/null
)), with zsh
(whose stat
builtin predates both GNU and FreeBSD stat
by the way (not IRIX' though))):
zmodload zsh/stat
if [ "$(stat +rdev -f 0)" = "$(stat +rdev /dev/null)" ]; then
echo stdin is open on the null device
fi
(note that it doesn't say if the file descriptor was open in read-only, write-only or read+write mode).
To check that it's open on the current /dev/null
file specifically (not /some/chroot/dev/null
for instance), on Linux only (where /dev/stdin
is implemented as a symlink to the file open on fd 0 instead of a special device which when open acts like a dup(0)
in other systems):
if [ /dev/stdin -ef /dev/null ]; then
echo stdin is open on /dev/null
fi
On non-Linux, you can try:
if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
echo stdin is open on /dev/null
fi
add a comment |
up vote
0
down vote
In an EDIT you supplied this code as an example to reproduce the problem:
# mknod secretunknownname c 1 3
# exec 6<secretunknownname
# rm secretunknownname
# ./checkstdinnull <&6
yes
I'll code that into two functions (insecure, don't use unless you do know what you are doing):
# f(){ f=secretunknownname; mknod "$f" c "$1" "$2"; exec 6<$f; rm "$f"; }
# checkstdinnull(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" && echo yes || echo no; }
It works for the test cases you presented:
# checkstdinnull
no
# checkstdinnull < /dev/null
yes
# echo -n | ./checkstdinnull
no
And (apparently) for your edit:
# f 1 3
# checkstdinnull <&6
yes
But that doesn't actually check for /dev/null
, only that the fd of the redirected file was 1:3 (it got deleted).
Try:
# f 1 5 # not /dev/null
# checkstdinnull <&6
no
you seem to need something a bit more robust:
# checkstdinnull(){
f="$(readlink -f /dev/stdin)";
if [ "$f" = /dev/null ] || [ "$f" != "${f%(deleted)*}" ];
then echo yes; else echo no; fi;
}
# checkstdinnull
no
# checkstdinnull </dev/null
yes
# echo -n | checkstdinnull
no
# f 1 3
# checkstdinnull <&6
yes
# f 1 5
# checkstdinnull <&6
yes
Note: this may fail positive if the device name contains (deleted)
(very unlikely but worth knowing).
add a comment |
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
14
down vote
accepted
On linux, you can do it with:
stdin_is_dev_null(){ test "`stat -Lc %t:%T /dev/stdin`" = "`stat -Lc %t:%T /dev/null`"; }
On a linux without stat(1) (eg. the busybox on your 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`"; }
On systems like *bsd and solaris, /dev/stdin
, /dev/fd/0
and /proc/PID/fd/0
are not "magical" symlinks as on linux, but character devices which will switch to the real file when opened. A stat(2) on their path will return something different than a fstat(2) on the opened file descriptor.
This means that the linux example will not work there, even with GNU coreutils installed. If the versions of GNU stat(1) is recent enough, you can use the -
argument to let it do a fstat(2) on the file descriptor 0, just like the stat(1) from *bsd:
stdin_is_dev_null(){ test "`stat -Lc %t:%T -`" = "`stat -Lc %t:%T /dev/null`"; }
It's also very easy to do the check portably in any language which offers an interface to fstat(2), eg. in perl
:
stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
13
Would strongly recommend$( ...)
instead of backticks.
– roaima
13 hours ago
8
Not suggesting another command expansion. Simply that you replace the backtick characters with$(
and)
.stdin_is_dev_null(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)"; }
.
– roaima
12 hours ago
3
@roaima, I was just wondering why you're strongly recommend$(...)
instead of backticks? I use backticks most of the times and it works fine except in case of nested of backticks where I prefer$(...)
?
– finn
12 hours ago
3
@roaima what bug does that fix? my point was that I know & agree that$(...)
is much more robust wrt embedded quotings and also can be easily nested, but in this case that's not necessary. So let's not increase the entropy of the universe with unnecessary changes ;-) Better think of some way to eliminate the repetition; something likestat -c %t:%T /dev/stdin /dev/null | uniq -u
, but lighter.
– mosvy
12 hours ago
8
FWIW the question was not about shell syntax. For what is concerning my issue, if the answer had only pointed me to thestat
command I would have already been quite satisfied. I don't see the point in starting an editing war between`
and$(
supporters. Personally, I prefer$(...)
and there are some POSIX rationales in favor of that syntax (pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) But I don't see the point in downvoting an otherwise correct answer for something not related with the question.
– Sylvain Leroux
11 hours ago
|
show 5 more comments
up vote
14
down vote
accepted
On linux, you can do it with:
stdin_is_dev_null(){ test "`stat -Lc %t:%T /dev/stdin`" = "`stat -Lc %t:%T /dev/null`"; }
On a linux without stat(1) (eg. the busybox on your 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`"; }
On systems like *bsd and solaris, /dev/stdin
, /dev/fd/0
and /proc/PID/fd/0
are not "magical" symlinks as on linux, but character devices which will switch to the real file when opened. A stat(2) on their path will return something different than a fstat(2) on the opened file descriptor.
This means that the linux example will not work there, even with GNU coreutils installed. If the versions of GNU stat(1) is recent enough, you can use the -
argument to let it do a fstat(2) on the file descriptor 0, just like the stat(1) from *bsd:
stdin_is_dev_null(){ test "`stat -Lc %t:%T -`" = "`stat -Lc %t:%T /dev/null`"; }
It's also very easy to do the check portably in any language which offers an interface to fstat(2), eg. in perl
:
stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
13
Would strongly recommend$( ...)
instead of backticks.
– roaima
13 hours ago
8
Not suggesting another command expansion. Simply that you replace the backtick characters with$(
and)
.stdin_is_dev_null(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)"; }
.
– roaima
12 hours ago
3
@roaima, I was just wondering why you're strongly recommend$(...)
instead of backticks? I use backticks most of the times and it works fine except in case of nested of backticks where I prefer$(...)
?
– finn
12 hours ago
3
@roaima what bug does that fix? my point was that I know & agree that$(...)
is much more robust wrt embedded quotings and also can be easily nested, but in this case that's not necessary. So let's not increase the entropy of the universe with unnecessary changes ;-) Better think of some way to eliminate the repetition; something likestat -c %t:%T /dev/stdin /dev/null | uniq -u
, but lighter.
– mosvy
12 hours ago
8
FWIW the question was not about shell syntax. For what is concerning my issue, if the answer had only pointed me to thestat
command I would have already been quite satisfied. I don't see the point in starting an editing war between`
and$(
supporters. Personally, I prefer$(...)
and there are some POSIX rationales in favor of that syntax (pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) But I don't see the point in downvoting an otherwise correct answer for something not related with the question.
– Sylvain Leroux
11 hours ago
|
show 5 more comments
up vote
14
down vote
accepted
up vote
14
down vote
accepted
On linux, you can do it with:
stdin_is_dev_null(){ test "`stat -Lc %t:%T /dev/stdin`" = "`stat -Lc %t:%T /dev/null`"; }
On a linux without stat(1) (eg. the busybox on your 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`"; }
On systems like *bsd and solaris, /dev/stdin
, /dev/fd/0
and /proc/PID/fd/0
are not "magical" symlinks as on linux, but character devices which will switch to the real file when opened. A stat(2) on their path will return something different than a fstat(2) on the opened file descriptor.
This means that the linux example will not work there, even with GNU coreutils installed. If the versions of GNU stat(1) is recent enough, you can use the -
argument to let it do a fstat(2) on the file descriptor 0, just like the stat(1) from *bsd:
stdin_is_dev_null(){ test "`stat -Lc %t:%T -`" = "`stat -Lc %t:%T /dev/null`"; }
It's also very easy to do the check portably in any language which offers an interface to fstat(2), eg. in perl
:
stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
On linux, you can do it with:
stdin_is_dev_null(){ test "`stat -Lc %t:%T /dev/stdin`" = "`stat -Lc %t:%T /dev/null`"; }
On a linux without stat(1) (eg. the busybox on your 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`"; }
On systems like *bsd and solaris, /dev/stdin
, /dev/fd/0
and /proc/PID/fd/0
are not "magical" symlinks as on linux, but character devices which will switch to the real file when opened. A stat(2) on their path will return something different than a fstat(2) on the opened file descriptor.
This means that the linux example will not work there, even with GNU coreutils installed. If the versions of GNU stat(1) is recent enough, you can use the -
argument to let it do a fstat(2) on the file descriptor 0, just like the stat(1) from *bsd:
stdin_is_dev_null(){ test "`stat -Lc %t:%T -`" = "`stat -Lc %t:%T /dev/null`"; }
It's also very easy to do the check portably in any language which offers an interface to fstat(2), eg. in perl
:
stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }
edited 3 hours ago
answered 13 hours ago
mosvy
4,671322
4,671322
13
Would strongly recommend$( ...)
instead of backticks.
– roaima
13 hours ago
8
Not suggesting another command expansion. Simply that you replace the backtick characters with$(
and)
.stdin_is_dev_null(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)"; }
.
– roaima
12 hours ago
3
@roaima, I was just wondering why you're strongly recommend$(...)
instead of backticks? I use backticks most of the times and it works fine except in case of nested of backticks where I prefer$(...)
?
– finn
12 hours ago
3
@roaima what bug does that fix? my point was that I know & agree that$(...)
is much more robust wrt embedded quotings and also can be easily nested, but in this case that's not necessary. So let's not increase the entropy of the universe with unnecessary changes ;-) Better think of some way to eliminate the repetition; something likestat -c %t:%T /dev/stdin /dev/null | uniq -u
, but lighter.
– mosvy
12 hours ago
8
FWIW the question was not about shell syntax. For what is concerning my issue, if the answer had only pointed me to thestat
command I would have already been quite satisfied. I don't see the point in starting an editing war between`
and$(
supporters. Personally, I prefer$(...)
and there are some POSIX rationales in favor of that syntax (pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) But I don't see the point in downvoting an otherwise correct answer for something not related with the question.
– Sylvain Leroux
11 hours ago
|
show 5 more comments
13
Would strongly recommend$( ...)
instead of backticks.
– roaima
13 hours ago
8
Not suggesting another command expansion. Simply that you replace the backtick characters with$(
and)
.stdin_is_dev_null(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)"; }
.
– roaima
12 hours ago
3
@roaima, I was just wondering why you're strongly recommend$(...)
instead of backticks? I use backticks most of the times and it works fine except in case of nested of backticks where I prefer$(...)
?
– finn
12 hours ago
3
@roaima what bug does that fix? my point was that I know & agree that$(...)
is much more robust wrt embedded quotings and also can be easily nested, but in this case that's not necessary. So let's not increase the entropy of the universe with unnecessary changes ;-) Better think of some way to eliminate the repetition; something likestat -c %t:%T /dev/stdin /dev/null | uniq -u
, but lighter.
– mosvy
12 hours ago
8
FWIW the question was not about shell syntax. For what is concerning my issue, if the answer had only pointed me to thestat
command I would have already been quite satisfied. I don't see the point in starting an editing war between`
and$(
supporters. Personally, I prefer$(...)
and there are some POSIX rationales in favor of that syntax (pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) But I don't see the point in downvoting an otherwise correct answer for something not related with the question.
– Sylvain Leroux
11 hours ago
13
13
Would strongly recommend
$( ...)
instead of backticks.– roaima
13 hours ago
Would strongly recommend
$( ...)
instead of backticks.– roaima
13 hours ago
8
8
Not suggesting another command expansion. Simply that you replace the backtick characters with
$(
and )
. stdin_is_dev_null(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)"; }
.– roaima
12 hours ago
Not suggesting another command expansion. Simply that you replace the backtick characters with
$(
and )
. stdin_is_dev_null(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)"; }
.– roaima
12 hours ago
3
3
@roaima, I was just wondering why you're strongly recommend
$(...)
instead of backticks? I use backticks most of the times and it works fine except in case of nested of backticks where I prefer $(...)
?– finn
12 hours ago
@roaima, I was just wondering why you're strongly recommend
$(...)
instead of backticks? I use backticks most of the times and it works fine except in case of nested of backticks where I prefer $(...)
?– finn
12 hours ago
3
3
@roaima what bug does that fix? my point was that I know & agree that
$(...)
is much more robust wrt embedded quotings and also can be easily nested, but in this case that's not necessary. So let's not increase the entropy of the universe with unnecessary changes ;-) Better think of some way to eliminate the repetition; something like stat -c %t:%T /dev/stdin /dev/null | uniq -u
, but lighter.– mosvy
12 hours ago
@roaima what bug does that fix? my point was that I know & agree that
$(...)
is much more robust wrt embedded quotings and also can be easily nested, but in this case that's not necessary. So let's not increase the entropy of the universe with unnecessary changes ;-) Better think of some way to eliminate the repetition; something like stat -c %t:%T /dev/stdin /dev/null | uniq -u
, but lighter.– mosvy
12 hours ago
8
8
FWIW the question was not about shell syntax. For what is concerning my issue, if the answer had only pointed me to the
stat
command I would have already been quite satisfied. I don't see the point in starting an editing war between `
and $(
supporters. Personally, I prefer $(...)
and there are some POSIX rationales in favor of that syntax (pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) But I don't see the point in downvoting an otherwise correct answer for something not related with the question.– Sylvain Leroux
11 hours ago
FWIW the question was not about shell syntax. For what is concerning my issue, if the answer had only pointed me to the
stat
command I would have already been quite satisfied. I don't see the point in starting an editing war between `
and $(
supporters. Personally, I prefer $(...)
and there are some POSIX rationales in favor of that syntax (pubs.opengroup.org/onlinepubs/9699919799/xrat/… ) But I don't see the point in downvoting an otherwise correct answer for something not related with the question.– Sylvain Leroux
11 hours ago
|
show 5 more comments
up vote
14
down vote
On Linux, to determine whether standard input is redirected from /dev/null
, you can check whether /proc/self/fd/0
has the same device and inode as /dev/null
:
if [ /proc/self/fd/0 -ef /dev/null ]; then echo yes; else echo no; fi
You can use /dev/stdin
instead of /proc/self/fd/0
.
If you want to check whether standard input is redirected from the null device, you need to compare major and minor device numbers, for example using stat
(see also mosvy’s answer):
if [ "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" ]; then echo yes; else echo no; fi
or, if you don’t care about this being Linux-specific,
if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
1
-ef, True if FILE1 and FILE2 refer to the same device and inode
– Rui F Ribeiro
13 hours ago
very cool.bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'
then do that again with</dev/null
. +1
– glenn jackman
11 hours ago
1
The/dev/stdin
being a symlink to the original file is specific to Linux, so all those solutions are Linux-specific anyway. Thestat
-based ones require GNU or busyboxstat
. With recent versions of GNUstat
, you can usestat -
to do afstat()
on fd 0 which would then work on non-Linux systems.
– Stéphane Chazelas
9 hours ago
@Stéphane the last part is exactly the situation the OP ran into, which is why I wrote up both solutions, distinguishing between/dev/null
and the null device.
– Stephen Kitt
9 hours ago
Oops, missed that.
– Stéphane Chazelas
7 hours ago
add a comment |
up vote
14
down vote
On Linux, to determine whether standard input is redirected from /dev/null
, you can check whether /proc/self/fd/0
has the same device and inode as /dev/null
:
if [ /proc/self/fd/0 -ef /dev/null ]; then echo yes; else echo no; fi
You can use /dev/stdin
instead of /proc/self/fd/0
.
If you want to check whether standard input is redirected from the null device, you need to compare major and minor device numbers, for example using stat
(see also mosvy’s answer):
if [ "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" ]; then echo yes; else echo no; fi
or, if you don’t care about this being Linux-specific,
if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
1
-ef, True if FILE1 and FILE2 refer to the same device and inode
– Rui F Ribeiro
13 hours ago
very cool.bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'
then do that again with</dev/null
. +1
– glenn jackman
11 hours ago
1
The/dev/stdin
being a symlink to the original file is specific to Linux, so all those solutions are Linux-specific anyway. Thestat
-based ones require GNU or busyboxstat
. With recent versions of GNUstat
, you can usestat -
to do afstat()
on fd 0 which would then work on non-Linux systems.
– Stéphane Chazelas
9 hours ago
@Stéphane the last part is exactly the situation the OP ran into, which is why I wrote up both solutions, distinguishing between/dev/null
and the null device.
– Stephen Kitt
9 hours ago
Oops, missed that.
– Stéphane Chazelas
7 hours ago
add a comment |
up vote
14
down vote
up vote
14
down vote
On Linux, to determine whether standard input is redirected from /dev/null
, you can check whether /proc/self/fd/0
has the same device and inode as /dev/null
:
if [ /proc/self/fd/0 -ef /dev/null ]; then echo yes; else echo no; fi
You can use /dev/stdin
instead of /proc/self/fd/0
.
If you want to check whether standard input is redirected from the null device, you need to compare major and minor device numbers, for example using stat
(see also mosvy’s answer):
if [ "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" ]; then echo yes; else echo no; fi
or, if you don’t care about this being Linux-specific,
if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
On Linux, to determine whether standard input is redirected from /dev/null
, you can check whether /proc/self/fd/0
has the same device and inode as /dev/null
:
if [ /proc/self/fd/0 -ef /dev/null ]; then echo yes; else echo no; fi
You can use /dev/stdin
instead of /proc/self/fd/0
.
If you want to check whether standard input is redirected from the null device, you need to compare major and minor device numbers, for example using stat
(see also mosvy’s answer):
if [ "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" ]; then echo yes; else echo no; fi
or, if you don’t care about this being Linux-specific,
if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi
edited 6 hours ago
answered 13 hours ago
Stephen Kitt
158k23345421
158k23345421
1
-ef, True if FILE1 and FILE2 refer to the same device and inode
– Rui F Ribeiro
13 hours ago
very cool.bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'
then do that again with</dev/null
. +1
– glenn jackman
11 hours ago
1
The/dev/stdin
being a symlink to the original file is specific to Linux, so all those solutions are Linux-specific anyway. Thestat
-based ones require GNU or busyboxstat
. With recent versions of GNUstat
, you can usestat -
to do afstat()
on fd 0 which would then work on non-Linux systems.
– Stéphane Chazelas
9 hours ago
@Stéphane the last part is exactly the situation the OP ran into, which is why I wrote up both solutions, distinguishing between/dev/null
and the null device.
– Stephen Kitt
9 hours ago
Oops, missed that.
– Stéphane Chazelas
7 hours ago
add a comment |
1
-ef, True if FILE1 and FILE2 refer to the same device and inode
– Rui F Ribeiro
13 hours ago
very cool.bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'
then do that again with</dev/null
. +1
– glenn jackman
11 hours ago
1
The/dev/stdin
being a symlink to the original file is specific to Linux, so all those solutions are Linux-specific anyway. Thestat
-based ones require GNU or busyboxstat
. With recent versions of GNUstat
, you can usestat -
to do afstat()
on fd 0 which would then work on non-Linux systems.
– Stéphane Chazelas
9 hours ago
@Stéphane the last part is exactly the situation the OP ran into, which is why I wrote up both solutions, distinguishing between/dev/null
and the null device.
– Stephen Kitt
9 hours ago
Oops, missed that.
– Stéphane Chazelas
7 hours ago
1
1
-ef, True if FILE1 and FILE2 refer to the same device and inode
– Rui F Ribeiro
13 hours ago
-ef, True if FILE1 and FILE2 refer to the same device and inode
– Rui F Ribeiro
13 hours ago
very cool.
bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'
then do that again with </dev/null
. +1– glenn jackman
11 hours ago
very cool.
bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'
then do that again with </dev/null
. +1– glenn jackman
11 hours ago
1
1
The
/dev/stdin
being a symlink to the original file is specific to Linux, so all those solutions are Linux-specific anyway. The stat
-based ones require GNU or busybox stat
. With recent versions of GNU stat
, you can use stat -
to do a fstat()
on fd 0 which would then work on non-Linux systems.– Stéphane Chazelas
9 hours ago
The
/dev/stdin
being a symlink to the original file is specific to Linux, so all those solutions are Linux-specific anyway. The stat
-based ones require GNU or busybox stat
. With recent versions of GNU stat
, you can use stat -
to do a fstat()
on fd 0 which would then work on non-Linux systems.– Stéphane Chazelas
9 hours ago
@Stéphane the last part is exactly the situation the OP ran into, which is why I wrote up both solutions, distinguishing between
/dev/null
and the null device.– Stephen Kitt
9 hours ago
@Stéphane the last part is exactly the situation the OP ran into, which is why I wrote up both solutions, distinguishing between
/dev/null
and the null device.– Stephen Kitt
9 hours ago
Oops, missed that.
– Stéphane Chazelas
7 hours ago
Oops, missed that.
– Stéphane Chazelas
7 hours ago
add a comment |
up vote
1
down vote
Portably, to check that stdin is the null
device (open on /dev/null
or not (like a copy of /dev/null
)), with zsh
(whose stat
builtin predates both GNU and FreeBSD stat
by the way (not IRIX' though))):
zmodload zsh/stat
if [ "$(stat +rdev -f 0)" = "$(stat +rdev /dev/null)" ]; then
echo stdin is open on the null device
fi
(note that it doesn't say if the file descriptor was open in read-only, write-only or read+write mode).
To check that it's open on the current /dev/null
file specifically (not /some/chroot/dev/null
for instance), on Linux only (where /dev/stdin
is implemented as a symlink to the file open on fd 0 instead of a special device which when open acts like a dup(0)
in other systems):
if [ /dev/stdin -ef /dev/null ]; then
echo stdin is open on /dev/null
fi
On non-Linux, you can try:
if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
echo stdin is open on /dev/null
fi
add a comment |
up vote
1
down vote
Portably, to check that stdin is the null
device (open on /dev/null
or not (like a copy of /dev/null
)), with zsh
(whose stat
builtin predates both GNU and FreeBSD stat
by the way (not IRIX' though))):
zmodload zsh/stat
if [ "$(stat +rdev -f 0)" = "$(stat +rdev /dev/null)" ]; then
echo stdin is open on the null device
fi
(note that it doesn't say if the file descriptor was open in read-only, write-only or read+write mode).
To check that it's open on the current /dev/null
file specifically (not /some/chroot/dev/null
for instance), on Linux only (where /dev/stdin
is implemented as a symlink to the file open on fd 0 instead of a special device which when open acts like a dup(0)
in other systems):
if [ /dev/stdin -ef /dev/null ]; then
echo stdin is open on /dev/null
fi
On non-Linux, you can try:
if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
echo stdin is open on /dev/null
fi
add a comment |
up vote
1
down vote
up vote
1
down vote
Portably, to check that stdin is the null
device (open on /dev/null
or not (like a copy of /dev/null
)), with zsh
(whose stat
builtin predates both GNU and FreeBSD stat
by the way (not IRIX' though))):
zmodload zsh/stat
if [ "$(stat +rdev -f 0)" = "$(stat +rdev /dev/null)" ]; then
echo stdin is open on the null device
fi
(note that it doesn't say if the file descriptor was open in read-only, write-only or read+write mode).
To check that it's open on the current /dev/null
file specifically (not /some/chroot/dev/null
for instance), on Linux only (where /dev/stdin
is implemented as a symlink to the file open on fd 0 instead of a special device which when open acts like a dup(0)
in other systems):
if [ /dev/stdin -ef /dev/null ]; then
echo stdin is open on /dev/null
fi
On non-Linux, you can try:
if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
echo stdin is open on /dev/null
fi
Portably, to check that stdin is the null
device (open on /dev/null
or not (like a copy of /dev/null
)), with zsh
(whose stat
builtin predates both GNU and FreeBSD stat
by the way (not IRIX' though))):
zmodload zsh/stat
if [ "$(stat +rdev -f 0)" = "$(stat +rdev /dev/null)" ]; then
echo stdin is open on the null device
fi
(note that it doesn't say if the file descriptor was open in read-only, write-only or read+write mode).
To check that it's open on the current /dev/null
file specifically (not /some/chroot/dev/null
for instance), on Linux only (where /dev/stdin
is implemented as a symlink to the file open on fd 0 instead of a special device which when open acts like a dup(0)
in other systems):
if [ /dev/stdin -ef /dev/null ]; then
echo stdin is open on /dev/null
fi
On non-Linux, you can try:
if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
echo stdin is open on /dev/null
fi
answered 9 hours ago


Stéphane Chazelas
294k54555898
294k54555898
add a comment |
add a comment |
up vote
0
down vote
In an EDIT you supplied this code as an example to reproduce the problem:
# mknod secretunknownname c 1 3
# exec 6<secretunknownname
# rm secretunknownname
# ./checkstdinnull <&6
yes
I'll code that into two functions (insecure, don't use unless you do know what you are doing):
# f(){ f=secretunknownname; mknod "$f" c "$1" "$2"; exec 6<$f; rm "$f"; }
# checkstdinnull(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" && echo yes || echo no; }
It works for the test cases you presented:
# checkstdinnull
no
# checkstdinnull < /dev/null
yes
# echo -n | ./checkstdinnull
no
And (apparently) for your edit:
# f 1 3
# checkstdinnull <&6
yes
But that doesn't actually check for /dev/null
, only that the fd of the redirected file was 1:3 (it got deleted).
Try:
# f 1 5 # not /dev/null
# checkstdinnull <&6
no
you seem to need something a bit more robust:
# checkstdinnull(){
f="$(readlink -f /dev/stdin)";
if [ "$f" = /dev/null ] || [ "$f" != "${f%(deleted)*}" ];
then echo yes; else echo no; fi;
}
# checkstdinnull
no
# checkstdinnull </dev/null
yes
# echo -n | checkstdinnull
no
# f 1 3
# checkstdinnull <&6
yes
# f 1 5
# checkstdinnull <&6
yes
Note: this may fail positive if the device name contains (deleted)
(very unlikely but worth knowing).
add a comment |
up vote
0
down vote
In an EDIT you supplied this code as an example to reproduce the problem:
# mknod secretunknownname c 1 3
# exec 6<secretunknownname
# rm secretunknownname
# ./checkstdinnull <&6
yes
I'll code that into two functions (insecure, don't use unless you do know what you are doing):
# f(){ f=secretunknownname; mknod "$f" c "$1" "$2"; exec 6<$f; rm "$f"; }
# checkstdinnull(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" && echo yes || echo no; }
It works for the test cases you presented:
# checkstdinnull
no
# checkstdinnull < /dev/null
yes
# echo -n | ./checkstdinnull
no
And (apparently) for your edit:
# f 1 3
# checkstdinnull <&6
yes
But that doesn't actually check for /dev/null
, only that the fd of the redirected file was 1:3 (it got deleted).
Try:
# f 1 5 # not /dev/null
# checkstdinnull <&6
no
you seem to need something a bit more robust:
# checkstdinnull(){
f="$(readlink -f /dev/stdin)";
if [ "$f" = /dev/null ] || [ "$f" != "${f%(deleted)*}" ];
then echo yes; else echo no; fi;
}
# checkstdinnull
no
# checkstdinnull </dev/null
yes
# echo -n | checkstdinnull
no
# f 1 3
# checkstdinnull <&6
yes
# f 1 5
# checkstdinnull <&6
yes
Note: this may fail positive if the device name contains (deleted)
(very unlikely but worth knowing).
add a comment |
up vote
0
down vote
up vote
0
down vote
In an EDIT you supplied this code as an example to reproduce the problem:
# mknod secretunknownname c 1 3
# exec 6<secretunknownname
# rm secretunknownname
# ./checkstdinnull <&6
yes
I'll code that into two functions (insecure, don't use unless you do know what you are doing):
# f(){ f=secretunknownname; mknod "$f" c "$1" "$2"; exec 6<$f; rm "$f"; }
# checkstdinnull(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" && echo yes || echo no; }
It works for the test cases you presented:
# checkstdinnull
no
# checkstdinnull < /dev/null
yes
# echo -n | ./checkstdinnull
no
And (apparently) for your edit:
# f 1 3
# checkstdinnull <&6
yes
But that doesn't actually check for /dev/null
, only that the fd of the redirected file was 1:3 (it got deleted).
Try:
# f 1 5 # not /dev/null
# checkstdinnull <&6
no
you seem to need something a bit more robust:
# checkstdinnull(){
f="$(readlink -f /dev/stdin)";
if [ "$f" = /dev/null ] || [ "$f" != "${f%(deleted)*}" ];
then echo yes; else echo no; fi;
}
# checkstdinnull
no
# checkstdinnull </dev/null
yes
# echo -n | checkstdinnull
no
# f 1 3
# checkstdinnull <&6
yes
# f 1 5
# checkstdinnull <&6
yes
Note: this may fail positive if the device name contains (deleted)
(very unlikely but worth knowing).
In an EDIT you supplied this code as an example to reproduce the problem:
# mknod secretunknownname c 1 3
# exec 6<secretunknownname
# rm secretunknownname
# ./checkstdinnull <&6
yes
I'll code that into two functions (insecure, don't use unless you do know what you are doing):
# f(){ f=secretunknownname; mknod "$f" c "$1" "$2"; exec 6<$f; rm "$f"; }
# checkstdinnull(){ test "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" && echo yes || echo no; }
It works for the test cases you presented:
# checkstdinnull
no
# checkstdinnull < /dev/null
yes
# echo -n | ./checkstdinnull
no
And (apparently) for your edit:
# f 1 3
# checkstdinnull <&6
yes
But that doesn't actually check for /dev/null
, only that the fd of the redirected file was 1:3 (it got deleted).
Try:
# f 1 5 # not /dev/null
# checkstdinnull <&6
no
you seem to need something a bit more robust:
# checkstdinnull(){
f="$(readlink -f /dev/stdin)";
if [ "$f" = /dev/null ] || [ "$f" != "${f%(deleted)*}" ];
then echo yes; else echo no; fi;
}
# checkstdinnull
no
# checkstdinnull </dev/null
yes
# echo -n | checkstdinnull
no
# f 1 3
# checkstdinnull <&6
yes
# f 1 5
# checkstdinnull <&6
yes
Note: this may fail positive if the device name contains (deleted)
(very unlikely but worth knowing).
answered 9 hours ago


Isaac
9,70311445
9,70311445
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f484228%2fhow-to-check-if-stdin-is-dev-null-from-the-shell%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
Do you need to know if it's
/dev/null
, or just that it's not a tty?– roaima
13 hours ago
The output of
{ readlink -f /dev/stdin; } <&6
for the case where you used exec and removed the node is/root/secretunknownname (deleted)
. As it shows that the file got deleted: Isn't that enough for what you need?– Isaac
11 hours ago
I need (actually "needed") to know if the standard input was the null device.
– Sylvain Leroux
11 hours ago
But checking for the
/dev/null
won't work forrm secretunknownname
, would it?– Isaac
11 hours ago
I am trying to find my way in a (very poorly designed!) industrial system. And it sometimes maps input of worker utility to the null device, or, some other times, to the actual hardware device. In both cases using the same code, but with different major/minor dev numbers. We are trying to track when (and why!) it sometimes chose to use one or the other. For now, the
stat
solution is the only one working.– Sylvain Leroux
11 hours ago