首页 > 解决方案 > 在 bash 中打印到终端输出上方的行

问题描述

编辑:更正进程/线程术语

我的 shell 脚本有一个读取用户输入的前台进程和一个打印消息的后台进程。我想在输入提示上方的行上打印这些消息,而不是中断输入。这是一个罐头示例:

sleep 5 && echo -e "\nINFO: Helpful Status Update!" &
echo -n "> "
read input

当我执行它并多次输入“输入”时,我得到如下信息:

> input input input inp
INFO: Helpful Status Update!
ut input

但我想看到这样的东西:

INFO: Helpful Status Update!
> input input input input input

该解决方案不需要是可移植的(我在 linux 上使用 bash),但我想尽可能避免使用 ncurses。

编辑:根据@Nick 的说法,由于历史原因,以前的行无法访问。但是,我的情况只需要修改当前行。这是一个概念证明:

# Make named pipe
mkfifo pipe
# Spawn background process
while true; do
        sleep 2
        echo -en "\033[1K\rINFO: Helpful Status Update!\n> `cat pipe`"
done &
# Start foreground user input
echo -n "> "
pid=-1
collected=""
IFS=""
while true; do
        read -n 1 c
        collected="$collected$c"
        # Named pipes block writes, so must do background process
        echo -n "$collected" >> pipe &
        # Kill last loop's (potentially) still-blocking pipe write
        if kill -0 $pid &> /dev/null; then
                kill $pid &> /dev/null
        fi
        pid=$!
done

主要产生正确的行为,但缺乏 CLI 细节,如退格和箭头导航。这些可能会被黑客入侵,但我仍然无法相信尚未开发出标准方法。

标签: bashterminal

解决方案


原始 ANSI 代码在 Linux(和 MacOS)上的 bash 终端中仍然有效,因此您可以使用 \033[F 其中 \033 是 ESCape 字符。您可以通过 control-V 后跟 ESCape 字符在 bash 终端中生成它。你应该看到^[出现。然后键入[F。如果您测试以下脚本:

echo "original line 1"
echo "^[[Fupdated line 1"
echo "line 2"
echo "line 3"

你应该看到输出:

updated line 1
line 2
line 3

编辑:

我忘了补充一点,在你的脚本中使用它会导致光标返回到行首,所以进一步的输入会覆盖你已经输入的内容。您可以使用键盘上的 control-R 使 bash 重新键入当前行并将光标返回到行尾。


推荐阅读