14

在编写 bash 脚本时。有时您正在运行一个打开另一个程序的命令,例如 npm、composer.. 等。但同时您需要使用read它来提示用户。

你不可避免地会遇到这种错误:

read: read error: 0: Resource temporarily unavailable

在做了一些研究之后,似乎有一个解决方案,方法是将那些操纵 bash 脚本的 STDIN 的程序的 STDIN 管道传输到 /dev/null。

就像是:

npm install </dev/null

其他研究表明,这与 STDIN 被设置为某种阻塞/无阻塞状态并且在程序完成后它没有被重置有关。

问题是有某种万无一失的优雅方式来读取用户提示的输入,而不会受到那些操纵 STDIN 的程序的影响,也不必寻找哪些程序需要将其 STDIN 重定向到 /dev/null。您甚至可能需要使用这些程序的 STDIN!

4

6 回答 6

9

通常重要的是要知道被调用的程序期望什么输入以及从哪里输入,因此对于那些不应该得到任何输入的人来说,从 /dev/null 重定向标准输入不是问题。

尽管如此,仍然可以对 shell 本身和所有调用的程序执行此操作。只需将标准输入移动到另一个文件描述符并在其位置打开 /dev/null 即可。像这样:

exec 3<&0 0</dev/null

上面复制了文件描述符3下的stdin文件描述符(0),然后打开/dev/null来替换它。

在此之后,任何尝试读取标准输入的调用命令都将从 /dev/null 读取。应该读取原始标准输入的程序应该从文件描述符 3 重定向。像这样:

read -r var 0<&3

<重定向运算符假定目标文件描述符为 0,如果省略,则上述两个命令可以这样编写:

exec 3<&0 </dev/null
read -r var <&3
于 2013-11-11T22:09:49.100 回答
7

发生这种情况时,从您的 bash shell 中运行 bash,然后退出它(从而返回到原始 bash shell)。我在https://github.com/fish-shell/fish-shell/issues/176中发现了这个技巧,它对我有用,似乎 bash 恢复了 STDIN 状态。例子:

bash> do something that exhibits the STDIN problem
bash> bash
bash> exit
bash> repeat something: STDIN problem fixed
于 2018-06-15T18:50:19.733 回答
6

我有一个类似的问题,但我正在运行的命令确实需要一个真正的 STDIN,/dev/null 还不够好。相反,我能够做到:

TTY=$(/usr/bin/tty)
cmd-using-stdin < $TTY
read -r var

或结合 spbnick 的回答:

TTY=$(/usr/bin/tty)
exec 3<&0 < $TTY
cmd-using-stdin
read -r var 0<&3`

3它为您留下一个干净的 STDIN,read0成为来自终端的命令的新流。

于 2015-01-22T21:54:17.970 回答
3

我有同样的问题。我通过像这样直接从 tty 读取来解决,重定向标准输入:

read -p "Play both [y]? " -n 1 -r </dev/tty

而不是简单地:

read -p "Play both [y]? " -n 1 -r

就我而言,使用exec 3<&0 ...不起作用。

于 2019-08-08T16:56:28.307 回答
2

这里建议使用重定向的答案很好。幸运的是,Bashread应该很快就不再需要这样的修复了。Readline 的作者 Chet Ramey 已经写了一个补丁: http: //gnu-bash.2382.n7.nabble.com/read-may-fail-due-to-nonblocking-stdin-td18519.html

但是,这个问题比readBash 中的命令更普遍。许多程序假定标准输入是阻塞的(例如,mimeopen),而一些程序在退出后使标准输入保持非阻塞(例如,cec-client)。Bash 没有内置方法来关闭非阻塞输入,因此,在这些情况下,您可以从命令行使用 Python:

$ python3 -c $'import os\nos.set_blocking(0, True)'

你也可以让 Python 打印之前的状态,这样它只能暂时改变:

$ o=$(python3 -c $'import os\nprint(os.get_blocking(0))\nos.set_blocking(0, True)')
$ somecommandthatreadsstdin
$ python3 -c $'import os\nos.set_blocking(0, '$o')'
于 2017-12-28T16:35:53.687 回答
2

显然(资源暂时不可用是)这是由退出但离开模式EAGAIN的程序引起的STDINnonblocking这是另一个解决方案(最容易编写脚本?):

perl -MFcntl -e 'fcntl STDIN, F_SETFL, fcntl(STDIN, F_GETFL, 0) & ~O_NONBLOCK'
于 2019-12-19T19:30:09.170 回答