带有非阻塞读取的Bash脚本

mic*_*con 8 bash scripting nonblocking

我想将一些数据发送到具有命名管道的根进程.这是脚本,效果很好:

#!/bin/sh
pipe=/tmp/ntp

if [[ ! -p $pipe ]]; then
    mknod -m 666 $pipe p
fi

while true
do
    if read line <$pipe; then
         /root/netman/extra/bin/ntpclient -s -h $line > $pipe 2>&1
    fi
done
Run Code Online (Sandbox Code Playgroud)

我实际上有几个像这样的脚本.我想将所有这些内容放在一个脚本中.问题是第一次"读取"时执行阻塞而我无法在单个进程中执行多次"读取".我有什么办法吗?是否可以进行"非阻塞"bash读取?

gab*_*uzo 18

Bash的read embedded命令有一个-t参数来设置超时:

-t timeout
    Cause read to time out and return failure if a complete line of input is not
    read within timeout seconds. This option has no effect if read is not reading
    input from the terminal or a pipe.
Run Code Online (Sandbox Code Playgroud)

这应该可以帮助您解决此问题.

编辑:

此解决方案有一些限制,因为手册页指示:如果读取不是从终端或管道读取输入,则此选项无效.

所以,如果我在/ tmp中创建一个管道:

mknod /tmp/pipe p
Run Code Online (Sandbox Code Playgroud)

直接从管道读取不起作用:

$ read -t 1 </tmp/pipe  ; echo $?
Run Code Online (Sandbox Code Playgroud)

永远挂起.

$ cat /tmp/pipe | ( read -t 1 ; echo $? )
1
Run Code Online (Sandbox Code Playgroud)

它正在发挥作用,但猫并没有退出.

解决方案是将管道分配给文件描述符:

$ exec 7<>/tmp/pipe
Run Code Online (Sandbox Code Playgroud)

然后使用重定向从此文件描述符中读取:

$ read -t 1 <&7  ; echo $?
1
Run Code Online (Sandbox Code Playgroud)

-u选项read:

$ read -t 1 -u 7  ; echo $?
1
Run Code Online (Sandbox Code Playgroud)

  • 没有必要使用`exec`.诀窍是,你的建议`exec 7 <> FILE`在读/写模式下打开FILE,从而避免在管道上阻塞,如果没有编写器.但是,在读写模式下打开管道也可以直接使用read builtin:`read -t 1 <>/tmp/pipe line`读取一行并等待该行最多一秒钟. (5认同)

小智 3

只是将阅读周期放入后台(完成后添加&)?

  • @GordonDavisson bash 的子 shell 实例可以在现代 UNIX 系统上利用 fork() 的 COW 语义,而单独 fork-exec-ed bash 实例则不能。 (2认同)