使用管道时,bash“读取”超时不等待输入

时间:2018-11-20 11:06:22

标签: bash pipe

## bash script  
foo(){

    sleep 0.0001
    read -s -t 0.0002 var  ## get the pipeline input if have 
    if [ -n "$var" ];then
        echo "has pipe input"
    fi

    if [ $# -gt 0 ];then
        echo "has std input"
    fi
    read -s -t 30 var1  #1 # expect wait for 30 seconds , but acturaly not
    echo "failed"     
    read -s -t 30 var2  #2  
    echo "failed again"
}

echo "ok1"|  foo "ok"

## output: 
has pipe input
has std input
failed
failed again

如果foo有管道输入,则#1和#2处的读取命令将立即返回,而无需等待输入TIMEOUT

在我的真实脚本中,有三个需求:

1个使函数可以同时接受管道输入和参数(因为我希望我的函数可以从管道输入中进行配置,所以我认为这对我很好。例如:)

foo(){
    local config
    sleep 0.0001
    read -t 0.0002 config
    eval "$config"
}

then i can pass configuration like this 
foo para1 para2 <<EOF
G_TIMEOUT=300
G_ROW_SPACE=2
G_LINE_NUM=10
EOF

2在我的功能中,我需要从键盘读取用户输入(我需要使用 read 与用户进行交互)

3等待用户输入应该有一个超时(我想添加一个屏幕保护程序,如果在用户经过TIMEOUT秒后没有任何动作,将调用屏幕保护程序脚本,并且在按下任何键后,屏幕保护程序将返回,并再次等待使用输入

  

如果有一种方法可以在我获得管道输入后将管道输入重定向到fd3,然后关闭fd3以使管道断开,然后将fd0重新打开到标准输入(键盘)并等待用户输入?

3 个答案:

答案 0 :(得分:1)

它不等待输入,因为它已经到达管道的末端。

echo "ok1" | ...将单个行写入管道,然后将其关闭。函数中的第一个readok1读入var。所有其他read调用将立即返回,因为没有更多的数据可读取,并且以后由于管道的写端已被关闭而没有更多的数据出现。

如果您希望管道保持打开状态,则必须执行类似的操作

{ echo ok1; sleep 40; echo ok2; } | foo

答案 1 :(得分:0)

作为answer of melpomene的附加内容,您可以在执行以下行时看到:

$ echo foo | { read -t 10 var1; echo $?; read -t 10 var2; echo $?; }
0
1
$ read -t 1 var; echo $?
142

此行输出read的返回码和手动状态

  

返回码为零,除非遇到文件结尾,读取超时(在这种情况下,返回码大于128)或无效                 文件描述符作为-u

的参数提供      

来源:man bash

由此可见,由于达到了EOF,因此第一个命令中的第二次读取失败。

答案 2 :(得分:0)

  

因为函数foo具有管道输入,所以在子进程中,输入fd自动重定向到管道,只需将标准输入重定向到键盘(/ proc / pid / 0),即可获得管道输入,将解决问题< / p>      

感谢那些家伙给我的提示,这不是读命令问题,而是fd问题。

foo(){                  
local config

sleep 0.0001
read -t 0.0002 config

if [ -n "$config" ];then
config=$(cat -)
fi
echo "$config"
exec 3</dev/tty
read -t 10 -u 3 input
echo "success!"
}
  

更好的方法:

foo(){                  
local config

sleep 0.0001
read -t 0.0002 config

if [ -n "$config" ];then
config=$(cat -)
fi
exec 0<&-   ## close current pipeline input 
exec 0</dev/tty   ##reopen input fd with standard input 

read -t 10 input  ##read will wait for input for keyboard :) good !
echo "success!"
}
  

此外,如果我可以检测到当前输入是管道输入还是标准输入,我不会使用read config来判断是否有管道输入,但是如何实现呢? [-t 0]是个好主意

     

更好的方法:

foo(){                  
local config

if [ ! -t 0 ];then
    config=$(cat -)
    exec 0<&-   ## close current pipeline input 
    exec 0</dev/tty   ##reopen input fd with standard input
fi

read -t 10 input  ##read will wait for input for keyboard :) great !
echo "success!"
}