Reputation: 13216
I have a bash script from which I want to access /dev/tty, but only when it's available.
When it's not available (in my case: when running my script in GitHub Actions) then when I try to access it I get /dev/tty: No such device or address
, and I'm trying to detect that in advance to avoid the error and provide fallback behaviour instead.
To do so I need a bash test that can detect cleanly this case, and which will work reliably across platforms (i.e. not using the tty
command, which has issues on Mac).
I'm currently using [[ -e "/dev/tty" ]]
which doesn't work - it appears to return true even on GitHub Actions, where it seems that /dev/tty exists but accessing it will fail. What should I use instead?
Upvotes: 9
Views: 4218
Reputation: 5322
Instead of spawning a new shell process to test if /dev/tty
can really be opened for writing (test -w
lies, you know?), you can try to redirect stdout to /dev/tty from a subshell like so:
if (exec < /dev/tty) ; then
# /dev/tty is available
else
# no tty is available
fi
This is POSIX syntax and should work in any shell.
Upvotes: 1
Reputation: 13216
After testing lots of promising but not quite perfect suggestions (see the other answers), I think I've found my own solution that does exactly fit my needs:
if sh -c ": >/dev/tty" >/dev/null 2>/dev/null; then
# /dev/tty is available and usable
else
# /dev/tty is not available
fi
To explain:
: >/dev/tty
does nothing (using the :
bash built-in) and outputs the nothing to /dev/tty, thereby checking that it exists & it's writable, but not actually producing any visible output. If this succeeds, we're good.
If we do that at the top level without a /dev/tty, bash itself produces a noisy error in our output, complaining about /dev/tty being unusable. This can't be redirected and silenced because it comes from bash itself, not the :
command.
Wrapping that with sh -c "..." >/dev/null 2>/dev/null
runs the test in a bash subshell, with stdout/stderr removed, and so silences all errors & warnings while still returning the overall exit code.
Suggestions for further improvements welcome. For reference, I'm testing this with setsid <command>
, which seems to be a good simulation of the TTY-less environment I'm having trouble with.
Upvotes: 9
Reputation: 14733
Beyond the other answers mentioned in this thread (and as an alternative to the other idea involving $-
, which did not seem to work for you), what about this other idea mentioned in the bash manual?
if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi
Upvotes: 0
Reputation: 26592
Try this approach :
if test "$(ps -p "$$" -o tty=)" = "?"; then
echo "/dev/tty is not available."
else
echo "/dev/tty is available."
fi
Upvotes: 1
Reputation: 14733
It seems that adapting this answer from this question on ServerFault (entitled How can I check in bash if a shell is running in interactive mode?, which is close to your question albeit not an exact duplicate) could be a solution for your use case.
So, could you try writing either:
[ -t 0 ] && [ -t 1 ] && echo your code
[ -t 0 ] && echo your code
?For completeness, here is one link documenting this POSIX flag -t
, which is thus portable:
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
-t file_descriptor
True if file descriptor number file_descriptor is open and is associated with a terminal.
False if file_descriptor is not a valid file descriptor number, or if file descriptor number file_descriptor is not open, or if it is open but is not associated with a terminal.
Furthermore, if you use bash
(not just a POSIX-compliant shell), you might want to combine this idea with the special 255
file descriptor number: [ -t 255 ]
.
Source: On Unix&Linux-SE,
That
255
file descriptor is an open handle to the controlling tty and is only used whenbash
is run in interactive mode. […]− In Bash, what is file descriptor 255 for, can I use it? (by @mosvy)
Upvotes: 1