检测 /dev/tty 是否可用和功能的跨平台方法

Cross-platform method to detect whether /dev/tty is available & functional

我有一个 bash 脚本,我想从中访问 /dev/tty,但只有当它可用时才可以。

当它不可用时(在我的例子中:当 运行 我的脚本在 GitHub 操作中)然后当我尝试访问它时我得到 /dev/tty: No such device or address,我正在尝试提前检测以避免错误并提供回退行为。

为此,我需要一个 bash 测试,它可以清楚地检测到这种情况,并且可以跨平台可靠地工作(即不使用 tty 命令,它有问题 on Mac).

我目前正在使用 [[ -e "/dev/tty" ]],但它不起作用 - 即使在 GitHub 操作上,它似乎 return 也是正确的,似乎 /dev/tty 存在,但是访问它会失败。我应该改用什么?

似乎适应this answer from this question on ServerFault(标题为如果shell在交互模式下是运行,我如何检查bash?,这与您的问题很接近,尽管不是完全重复)可能是您用例的解决方案。

那么,你可以试试写:

  • [ -t 0 ] && [ -t 1 ] && echo your code
  • [ -t 0 ] && echo your code ?

为了完整起见,这里有一个 link 记录了这个 POSIX 标志 -t,因此它是可移植的:

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.

此外,如果您使用 bash(不仅仅是 POSIX-compliant shell),您可能希望将这个想法与特殊的 255 文件描述符编号结合起来: [ -t 255 ].

来源:Unix&Linux-SE,

That 255 file descriptor is an open handle to the controlling tty and is only used when bash is run in interactive mode. […]

In Bash, what is file descriptor 255 for, can I use it? (by @mosvy)

试试这个方法:

if  test "$(ps -p "$$" -o tty=)" = "?"; then
    echo "/dev/tty is not available."
else
    echo "/dev/tty is available."
fi

除了此线程中提到的其他答案(以及作为涉及 $-), 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

在测试了很多有前途但不是很完美的建议(参见其他答案)之后,我想我已经找到了完全符合我需要的解决方案:

if sh -c ": >/dev/tty" >/dev/null 2>/dev/null; then
    # /dev/tty is available and usable
else
    # /dev/tty is not available
fi

说明:

: >/dev/tty 什么都不做(使用 : bash 内置)并将什么都输出到 /dev/tty,从而检查它是否存在并且它是可写的,但是实际上没有产生任何可见的输出。如果这成功了,我们就很好了。

如果我们在没有 /dev/tty 的顶层执行此操作,bash 本身会在我们的输出中产生一个嘈杂的错误,抱怨 /dev/tty 不可用。这不能被重定向和静音,因为它来自 bash 本身,而不是 : 命令。

sh -c "..." >/dev/null 2>/dev/null 包装它在 bash 子 shell 中运行测试,删除 stdout/stderr,因此在仍然返回整体退出代码的同时消除所有错误和警告。

欢迎提出进一步改进的建议。作为参考,我正在使用 setsid <command> 进行测试,这似乎很好地模拟了我遇到问题的无 TTY 环境。

而不是产生一个新的 shell 进程来测试 /dev/tty 是否可以 真的 可以打开写入(test -w 谎言,你知道?),您可以尝试将 stdout 从 subshell 重定向到 /dev/tty,如下所示:

if (exec < /dev/tty) ; then
  # /dev/tty is available
else
  # no tty is available
fi

这是 POSIX 语法,应该适用于任何 shell。