编写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!
答案 0 :(得分:6)
通常,知道调用的程序所期望的输入以及从哪里开始是很重要的,因此从/ dev / null重定向stdin对于那些不应该获取任何内容的人来说不是一个问题。
仍然可以为shell本身和所有被调用的程序执行此操作。只需将stdin移动到另一个文件描述符并在其位置打开/ dev / null。像这样:
exec 3<&0 0</dev/null
上面复制了文件描述符3下的stdin文件描述符(0),然后打开/ dev / null来替换它。
在此之后,任何尝试读取stdin的被调用命令都将从/ dev / null读取。应该读取原始stdin的程序应该从文件描述符3重定向。像这样:
read -r var 0<&3
<
重定向运算符假定目标文件描述符为0,如果省略,则上述两个命令可以这样写:
exec 3<&0 </dev/null
read -r var <&3
答案 1 :(得分:5)
我有一个类似的问题,但我运行的命令确实需要一个真正的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
中为read
留下一个干净的STDIN,0
和{{1}}成为命令终端的新流。
答案 2 :(得分:2)
当发生这种情况时,从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
答案 3 :(得分:1)
这里建议使用重定向的答案是好的。幸运的是,Bash的read
很快就不再需要这样的修复了。 Readline的作者Chet Ramey已经编写了一个补丁:http://gnu-bash.2382.n7.nabble.com/read-may-fail-due-to-nonblocking-stdin-td18519.html
但是,这个问题比Bash中的read
命令更普遍。许多程序假定stdin是阻塞的(例如,mimeopen
),并且一些程序在退出后使stdin无阻塞(例如,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')'
答案 4 :(得分:1)
我有同样的问题。我这样解决,方法是直接从tty读取,重定向标准输入:
read -p "Play both [y]? " -n 1 -r </dev/tty
而不是简单地:
read -p "Play both [y]? " -n 1 -r
就我而言,使用exec 3<&0 ...
无效。
答案 5 :(得分:1)
很显然(资源暂时不可用,是EAGAIN
),这是由退出程序却使STDIN
处于nonblocking
模式的程序引起的。
这是另一种解决方案(最容易编写脚本?):
perl -MFcntl -e 'fcntl STDIN, F_SETFL, fcntl(STDIN, F_GETFL, 0) & ~O_NONBLOCK'