从脚本管道stdout / stderr时,提示不显示读取-p

时间:2016-05-25 19:11:11

标签: bash shell

我有一个功能,旨在捕获命令的输出并缩进每一行:

indent_lines () {
  local line
  local indent="        "

  while IFS= read -r line || [[ -n "$line" ]]; do
      # remove \r and replace with \r$indent
      echo "$indent $(echo "$line" | sed "s/$(printf '\r')/$(printf '\r')$indent /g")"
  done
}

使用方法如下:

some command 2>&1 | indent_lines

来自some command 2>&1的整个输出通过管道输入到indent_lines函数中,每行输出都将缩进。除了在read -p内调用some command之外,这种情况有效,例如:

get_name () {
   echo "this is a line of output 1"
   echo "this is a line of output 2"
   echo "this is a line of output 3"
   read -p "enter your name: " user_input
   echo
   echo "$user_input is your name"
}

输出结果为:

$ get_name 2>&1 | indent_lines
$        this is a line of output 1
$        this is a line of output 2
$        this is a line of output 3
$

提示不会显示并挂起等待输入。

在暂停输入之前有没有办法让提示显示?

1 个答案:

答案 0 :(得分:3)

输入端的while read循环(与许多其他工具一样)一次处理一行。由于提示不是在最后打印换行符,因此循环不会处理它。

在较高的层面上,您有两种选择:

  • 避免使用提示管道
  • 添加换行符,以便刷新内容

由于get_name函数无法修改,因此它是规范的一部分,我们最终在这里做的是修改shell环境以改变{{1工作。

避免管道

read将其提示写入stderr。

如果要重定向提示,请重定向FD 2.

如果你想确保其他重定向(例如read -p,这会导致提示转到stdout - 正在被捕获)不适用,那么直接指向TTY :

2>&1

添加换行符

现在,如果您的目标是运行一个shell函数 - 您无法修改 - 一般情况下stderr被重定向但read -p "enter something" 2>/dev/tty 打印提示直接提示给TTY,可以< / em>完成,黑客攻击类似于以下内容:

read -p

...这样的:

reading_to_tty() {
  read() { builtin read "$@" 2>/dev/tty; }
  "$@"
  unset -f read
}

...将运行reading_to_tty get_name 2>&1 ,使用get_name命令(而不是其他命令)将stderr内容直接发送到TTY。

根据扩展讨论,确保将提示刷新到管道格式化的另一种方法是附加换行符。下面这样做,因此可以使用通过格式化功能的现有管道:

read

...以与上述相同的方式使用:

reading_with_prompt_newline() { 
  read() {
    if [[ $1 = -p ]]; then 
      set -- "$1" "$2"$'\n' "${@:3}" 
    fi 
    builtin read "$@" 
  } 
  "$@" 
  unset -f read 
}