【问题标题】:Cross-platform method to detect whether /dev/tty is available & functional检测 /dev/tty 是否可用和功能的跨平台方法
【发布时间】:2021-11-03 14:33:49
【问题描述】:

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

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

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

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

【问题讨论】:

  • 不知道为什么,但 $- 在我的 bash 中包含“hBH”,而不是 i,因此该示例不起作用。
  • bash 脚本想要访问 /dev/tty 的目的是什么?
  • @Philippe 从其他命令的管道内启动连接到 TTY 的 $EDITOR(因此无法直接访问 stdin/stdout)。当 /dev/tty 可用时效果很好,当它不可用时,我可以提供 OK 后备,但我需要能够检测到。

标签: linux bash macos shell tty


【解决方案1】:

在测试了许多有希望但不太完美的建议(请参阅其他答案)之后,我想我已经找到了完全符合我需求的解决方案:

if bash -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 本身,而不是 printf 命令。

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

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

【讨论】:

  • 好主意,你可以这样做bash -c ': >/dev/tty' ...
  • 那就更好了!谢谢@Philippe,我已经更新了答案
  • 确实,这似乎是一个简单便携的解决方案 :-)
【解决方案2】:

试试这个方法:

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

【讨论】:

  • 我已经做了一些测试,这似乎工作得很好!我投了赞成票,但我将在下面使用我自己稍微简单的答案,只使用 printf & bash,只是因为我有点担心像这样的复杂 ps 命令在 Mac 和其他奇怪的环境中可能会略有不同(请参阅以apple.stackexchange.com/questions/300864/… 为例)。
【解决方案3】:

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

那么,你可以试试写吗:

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

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

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html

-t file_descriptor
如果文件描述符编号 file_descriptor 已打开并与终端关联,则为真。
如果 file_descriptor 不是有效的文件描述符编号,或者文件描述符编号 file_descriptor 未打开,或者它已打开但未与终端关联,则为 False。

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

来源:在 Unix&Linux-SE 上,

255 文件描述符是控制 tty 的打开句柄,仅在 bash 以交互模式运行时使用。 […]

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

【讨论】:

  • 在我的例子中,stdin & stdout 是其他进程的管道,所以 -t 0 & -t 1 是假的,这是行不通的。不过,当一个可用的 TTY 可用时,我仍然想通过直接使用 /dev/tty 与控制 TTY 进行通信。当 /dev/tty 可用时,它可以正常工作!但不是在完全没有 TTY 的环境中。
  • 好的,但是你认为你可以调整我的建议并通过另一个 FD 号码“保持”标准输入可用,只是为了这个测试目的?
  • 怎么样?能给我举个例子吗?该脚本作为管道的一部分运行,因此它永远无法通过标准输入或任何 FD 访问 TTY,因此我无法“保留”它。
  • 例如,如果您使用 bash(不仅仅是 POSIX shell),我刚刚在 this SE thread 中看到特殊的 FD 编号 255(以使 /dev/tty 可用,即使 stdin /stdout 已被重定向)可能是您测试的解决方案(即[ -t 255 ]...)
  • FD 255 是一个选项,它可能会起作用。最好有一个选项来测试 /dev/tty 本身,而不依赖于 bash 内部实现细节。我真的更喜欢检查我想直接使用的 tty 设备的状态,而不是在另一个解决方法中打补丁。
【解决方案4】:

除了此线程中提到的其他答案(以及作为涉及$-which did not seem to work for you 的其他想法的替代方案)之外,bash manual 中提到的其他想法呢?

if [ -z "$PS1" ]; then
    echo This shell is not interactive
else
    echo This shell is interactive
fi

【讨论】:

  • 好建议,但不幸的是,这似乎不能可靠地工作......在我的测试中,当在 bash (v5) 中运行保存的脚本时,即使 $PS1 设置在终端本身和 /dev/tty 正在工作(另外:这实际上需要 ${PS1+x} 来处理它已设置但为空的情况)。
猜你喜欢
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-21
  • 2014-05-13
  • 1970-01-01
  • 2013-07-04
相关资源
最近更新 更多