bash 使用“读取”时如何启用向上/向下箭头键以显示以前的输入?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6788777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:25:12  来源:igfitidea点击:

How to enable the up/down arrow keys to show previous inputs when using `read`?

bashshell

提问by nachocab

I'm waiting for user input (using 'read') in an infinite loop and would like to have command history, that is being able to show previous inputs that were already entered, using the up and down arrow keys, instead of getting ^[[A and ^[[B. Is this possible?

我正在无限循环中等待用户输入(使用“读取”),并希望拥有命令历史记录,即能够使用向上和向下箭头键显示之前已经输入的输入,而不是获取 ^ [[A 和 ^[[B. 这可能吗?



Thanks to @l0b0 for your answer. It got me on the right direction. After playing with it for some time I've realized I also need the following two features, but I haven't managed to get them yet:

感谢@l0b0 的回答。它让我找到了正确的方向。玩了一段时间后,我意识到我还需要以下两个功能,但我还没有设法获得它们:

  • If I press up and add something to the previous command I would like to have the whole thing saved in the history, not just the addition. Example

    $ ./up_and_down
    Enter command: hello
    ENTER
    Enter command:
    Up
    Enter command: hello you
    ENTER
    Enter command:
    Up
    Enter command: you
    (instead of "hello you")

  • If I can't keep going up because I'm at the end of the history array, I don't want the cursor to move to the previous line, instead I want it to stay fixed.

  • 如果我按下并在上一个命令中添加一些内容,我希望将整个内容保存在历史记录中,而不仅仅是添加内容。例子

    $ ./up_and_down
    输入命令:hello
    ENTER
    输入命令:
    Up
    输入命令:hello you
    ENTER
    输入命令:
    Up
    输入命令:you
    (而不是“hello you”)

  • 如果因为我在历史数组的末尾而无法继续向上,我不希望光标移动到上一行,而是希望它保持固定。

This is what I have so far (up_and_down):

这是我到目前为止(up_and_down):

#!/usr/bin/env bash
set -o nounset -o errexit -o pipefail

read_history() {
    local char
    local string
    local esc=$'\e'
    local up=$'\e[A'
    local down=$'\e[B'
    local clear_line=$'\r\e[K'


    local history=()
    local -i history_index=0

    # Read one character at a time
    while IFS="" read -p "Enter command:" -n1 -s char ; do
        if [[ "$char" == "$esc" ]]; then 
            # Get the rest of the escape sequence (3 characters total)
            while read -n2 -s rest ; do
                char+="$rest"
                break
            done
        fi

        if [[ "$char" == "$up" && $history_index > 0 ]] ; then
            history_index+=-1
            echo -ne $clear_line${history[$history_index]}
        elif [[ "$char" == "$down" && $history_index < $((${#history[@]} - 1)) ]] ; then
            history_index+=1
            echo -ne $clear_line${history[$history_index]}
        elif [[ -z "$char" ]]; then # user pressed ENTER
            echo
            history+=( "$string" )
            string=
            history_index=${#history[@]}
        else
            echo -n "$char"
            string+="$char"
        fi
    done
}
read_history

回答by micha

Two solutions using the -eoption to the readcommand combined with the builtin historycommand:

使用命令-e选项read结合内置history命令的两种解决方案:

# version 1
while IFS="" read -r -e -d $'\n' -p 'input> ' line; do 
   echo "$line"
   history -s "$line"
done

# version 2
while IFS="" read -r -e -d $'\n' -p 'input> ' line; do 
   echo "$line"
   echo "$line" >> ~/.bash_history
   history -n
done

回答by l0b0

Interesting question - Here's the result so far:

有趣的问题 - 这是迄今为止的结果:

#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
read_history() {
    local char=
    local string=
    local -a history=( )
    local -i histindex=0

    # Read one character at a time
    while IFS= read -r -n 1 -s char
    do
        if [ "$char" == $'\x1b' ] # \x1b is the start of an escape sequence
        then
            # Get the rest of the escape sequence (3 characters total)
            while IFS= read -r -n 2 -s rest
            do
                char+="$rest"
                break
            done
        fi

        if [ "$char" == $'\x1b[A' ]
        then
            # Up
            if [ $histindex -gt 0 ]
            then
                histindex+=-1
                echo -ne "\r3[K${history[$histindex]}"
            fi
        elif [ "$char" == $'\x1b[B' ]
        then
            # Down
            if [ $histindex -lt $((${#history[@]} - 1)) ]
            then
                histindex+=1
                echo -ne "\r3[K${history[$histindex]}"
            fi
        elif [ -z "$char" ]
        then
            # Newline
            echo
            history+=( "$string" )
            string=
            histindex=${#history[@]}
        else
            echo -n "$char"
            string+="$char"
        fi
    done
}
read_history

回答by Lynch

I use rlwrapto enable readline feature in program that does not support it. May be you could try this. rlwrap stand for readline wrapper. This command intercept your key up and key down and replace the prompt whit previous commands.

我使用rlwrap在不支持它的程序中启用 readline 功能。也许你可以试试这个。rlwrap 代表 readline 包装器。此命令拦截您的向上键和向下键并替换先前命令的提示。

The sintax is simply rlwrap ./your-script.

语法很简单rlwrap ./your-script

回答by mat

Use the -eoption to the read command (and make sure readlineis configured to use the up/down arrow keys to loop through the command history).

使用-e读取命令的选项(并确保readline配置为使用向上/向下箭头键循环浏览命令历史记录)。

help read | less -p '-e'

回答by TC1

As far as I know, no. "up" and "down" are both just as good a symbol as any (for that matter, C-p and C-n do the same as "up" and "down" functionally in bash), and can be input as part of what you're trying to read.

据我所知,没有。"up" 和 "down" 都是和任何符号一样好的符号(就此而言,Cp 和 Cn 在功能上与 bash 中的 "up" 和 "down" 相同),并且可以作为您输入的一部分重新尝试阅读。

That is, assuming you mean the bash builtin read. You could check the manpage for any options, but I can't think of any hack that would do what you want, at least not right now...

也就是说,假设您的意思是 bash 内置read. 您可以查看联机帮助页以获取任何选项,但我想不出任何可以执行您想要的操作的 hack,至少现在不是...

EDIT: Seemed interesting enough. @ work now & don't have bash or the time for it, but it could be done by setting -n 1on read, then checking if you just read a "up" or "down" and using historyto get the command needed. You would probably have to invent a local var to count the "up"s and "down"s, then get the relevant command from historywith the appropriate offset, output it to the screen & if the next readreturns with an empty string, use that found command.

编辑:看起来很有趣。@ 现在工作 & 没有 bash 或时间,但可以通过设置-n 1on来完成read,然后检查您是否只是读取“向上”或“向下”并使用history来获取所需的命令。您可能必须发明一个本地变量来计算“向上”和“向下”的数量,然后history使用适当的偏移量从中获取相关命令,将其输出到屏幕,如果下一个read返回空字符串,请使用那个找到命令。

As I said though, can't test this atm and no idea if it'll work.

正如我所说,无法测试这个 atm,也不知道它是否会工作。