【问题标题】:bash 'read' timeout do not wait for input when using pipelinebash“读取”超时使用管道时不等待输入
【发布时间】:2018-11-20 11:06:21
【问题描述】:
## 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 重新打开到标准输入(键盘)并等待用户输入?

【问题讨论】:

  • 您为什么认为这是不正确的?您期望的行为是什么?您很可能误解了 read 的 -t 标志的含义。另请注意,read 将从标准输入而非参数中读取。
  • 实际上我的脚本需要 1 个函数可以同时接受管道输入和标准输入 2 在我的函数中我需要等待用户输入 3 等待用户输入超时
  • 那么你期待什么?
  • 我期望函数可以对变量进行管道输入,然后使用 read 获取用户键盘输入

标签: bash pipe


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

    感谢那些人给我这个线索,这不是读取命令问题,实际上是 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!"
    }
    

    此外,如果我可以检测到当前输入是管道输入还是标准输入,我不能使用读取配置来判断是否有管道输入,但是如何填充呢? [ -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!"
    }
    

    【讨论】:

      【解决方案3】:

      作为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。

      【讨论】:

      • 我的屏幕保护程序已捕获 142 ,错误处理程序已捕获 1 ,我已经考虑了读取的返回码,但我认为它不适用于我的情况,我需要阅读在函数中等待用户输入
      猜你喜欢
      • 2014-06-05
      • 1970-01-01
      • 1970-01-01
      • 2012-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-08
      • 2020-02-23
      相关资源
      最近更新 更多