如何使用行缓冲提示包装命令?

问题描述 投票:2回答:2

我正在寻找一个命令,它首先通过给定的命令生成一个进程,然后提示用户使用给定的提示字符串输入一行(具有readline功能),将输入的行传递给该进程,然后重复。该过程的任何输出都打印在提示行上方的行上,以防止混乱,因此提示始终是屏幕上的最后一行,但该过程可以随时输出内容。

例如,一个提示命令,使prompt -p "> " cat在输入每一行之前运行带有提示的cat。它看起来像这样:

$ prompt -p "> " cat
> hello
hello
> every time it's time for me to type, there's a prompt!
every time it's time for me to type, there's a prompt!
> for sure
for sure

也许你也可以指定命令输出的提示,如下所示:

$ prompt -p "[IN] " -o "[OUT] " grep hi
[IN] hello
[IN] this is another example
[OUT] this is another example
[IN] it sure is, i'm glad you know

我找到了rlwrap(https://github.com/hanslub42/rlwrap),它似乎使用readline功能进行行缓冲,但没有输入提示。

基本上,我想要一个命令,可以将对输入流进行操作的任何命令转换为友好的repl。

这几乎可以工作,但只要进程输出某些内容,光标就会在错误的位置结束:

CMD="grep hi" # as an example

prompt () {
    while true
    do
        printf "> \033\067"
        read -e line || break
        echo $line > $1
    done
}

prompt >(stdbuf -oL $CMD |
    stdbuf -oL sed 's/^/< /' |
    stdbuf -oL sed 's/^/'`echo -ne "\033[0;$(expr $(tput lines) - 1)r\033[$(expr $(tput lines) - 1);0H\033E"`'/;s/$/'`echo -ne "\033[0;$(tput lines)r\033\070\033M"`'/')

这是另一个清晰的例子。想象一下一个简单的irc客户端命令,它从stdin读取命令并将简单消息输出到stdout。它没有任何接口甚至是提示,只是简单地读取,并直接从stdin和stdout打印:

$ irc someserver
NOTICE (*): *** Looking up your hostname...
NOTICE (*): *** Found your hostname
001 (madeline): Welcome to someserver IRC!! madeline!madeline@somewhere
(...)
/join #box
JOIN (): #box
353 (madeline = #box): madeline @framboos
366 (madeline #box): End of /NAMES list.
hello!
<madeline> hello!
(5 seconds later)
<framboos> hii

使用prompt命令,它看起来更像是这样的:

$ prompt -p "[IN] " -o "[OUT] " irc someserver
[OUT] NOTICE (*): *** Looking up your hostname...
[OUT] NOTICE (*): *** Found your hostname
[OUT] 001 (madeline): Welcome to someserver IRC!! madeline!madeline@somewhere
(...)
[IN] /join #box
[OUT] JOIN (): #box
[OUT] 353 (madeline = #box): madeline @framboos
[OUT] 366 (madeline #box): End of /NAMES list.
[IN] hello!
[OUT] <madeline> hello!
(5 seconds later)
[OUT] <framboos> hii
[IN] 

关键是生成一个进程并且您输入的每一行都通过管道传输到同一个进程,它不会为每一行生成一个新进程。还要注意[IN]提示如何不被来自framboos的消息破坏,而是在提示上方的行上打印消息。上面提到的rlwrap程序正确地做到了这一点。我所知道的唯一缺少的是提示字符串。

bash prompt readline read-eval-print-loop
2个回答
0
投票

在您的脚本中,您可以定义以下prompt例程:

#!/bin/bash

prompt() {
    if [[ "$3" = "-o" ]]; then
        symbol1="$2"
        symbol2="$4"
        shift 4
        process="$*"
        while true; do
            printf "%s" "$symbol1"
            read -e line
            output=$( echo "$line" | $process )
            if [[ "$output" != "" ]]; then
                printf "%s" "$symbol2"
                echo "$output"
            fi
        done
    else
        symbol="$2"
        shift 2
        process="$*"
        while true; do
            printf "%s" "$symbol"
            read -e line
            echo "$line" | $process
        done
     fi
}

然后,您可以获取脚本文件以使例程可用:source ./file


用法示例:

测试1

$ prompt -p "> " cat
> hello
hello
> every time it's time for me to type, there's a prompt!
every time it's time for me to type, there's a prompt!
> for sure
for sure 

测试2

$ prompt -p "[IN] " -o "[OUT] " grep hi
[IN] hello
[IN] this is another example
[OUT] this is another example
[IN] it sure is, i'm glad you know

0
投票

首先,我错了rlwrap,你可以用它来提示:

rlwrap -S "> " grep hi

它运作得很好。但是如果在进程打印某些内容时输入了某些内容,则会留下提示的文物。

然后我找到了socat,它可以做与上面相同的事情(除此之外),但它不会留下那些文物(通过在你输入时阻止标准输出直到你按下回车并且线条再次清晰):

socat READLINE,prompt="[IN] " EXEC:"stdbuf -oL grep hi"
[IN] hello
[IN] this is another example
this is another example
[IN] it sure is, i'm glad you know

然后我可以使用sed为输出添加提示:

socat READLINE,prompt="[IN] " SYSTEM:"stdbuf -oL grep hi | stdbuf -oL sed \'s/^/[OUT] /\'"
[IN] hello
[IN] this is another example
[OUT] this is another example
[IN] it sure is, i'm glad you know
© www.soinside.com 2019 - 2024. All rights reserved.