Подтвердить что ты не робот

Как включить клавиши со стрелками вверх/вниз для отображения предыдущих входов при использовании `read`?

Я жду ввода пользователя (используя 'read') в бесконечном цикле и хотел бы иметь историю команд, которая может показывать предыдущие входы, которые уже были введены, используя клавиши со стрелками вверх и вниз получения ^ [[A и ^ [[B. Возможно ли это?


Спасибо @l0b0 за ваш ответ. Это привело меня в правильном направлении. Поиграв с ним в течение некоторого времени, я понял, что мне также нужны следующие две функции, но мне еще не удалось их получить:

  • Если я нажму и добавлю что-то к предыдущей команде, я бы хотел, чтобы все это было сохранено в истории, а не просто добавление. Пример

    $./up_and_down
    Введите команду: hello
    ENTER
    Введите команду:
    Up
    Введите команду: привет, вы ENTER
    Введите команду:
    Up
    Введите команду: вы
    (вместо "привет, ты" )

  • Если я не могу продолжать расти, потому что я в конце массива истории, я не хочу, чтобы курсор переместился в предыдущую строку, вместо этого я хочу, чтобы он оставался фиксированным.

Это то, что я до сих пор (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
4b9b3361

Ответ 1

Два решения, использующие параметр -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

Ответ 2

Я использую rlwrap, чтобы включить функцию readline в программе, которая ее не поддерживает. Может быть, вы могли бы попробовать это. Подставка rlwrap для оболочки readline. Эта команда перехватывает ваш ключ вверх и вниз и заменяет подсказку с предыдущими командами.

Синтакс просто rlwrap ./your-script.

Ответ 3

Используйте параметр -e для команды чтения (и убедитесь, что readline настроен на использование клавиш со стрелками вверх/вниз для прокрутки истории команд).

help read | less -p '-e'

Ответ 4

Интересный вопрос - вот результат:

#!/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 "\r\033[K${history[$histindex]}"
            fi
        elif [ "$char" == $'\x1b[B' ]
        then
            # Down
            if [ $histindex -lt $((${#history[@]} - 1)) ]
            then
                histindex+=1
                echo -ne "\r\033[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

Ответ 5

Насколько я знаю, нет. "вверх" и "вниз" являются столь же хорошими символами, как и любые (в этом случае Cp и Cn выполняют те же функции, что и "вверх" и "вниз" в bash), и могут быть введены как часть того, что вы пытаетесь прочитать.

То есть, предполагая, что вы имеете в виду bash встроенный read. Вы можете проверить man-страницу для каких-либо параметров, но я не могу придумать какой-либо взлом, который сделает то, что вы хотите, по крайней мере, сейчас...

РЕДАКТ: Похоже, достаточно интересно. @теперь работаем и не имеют bash или времени для него, но это можно сделать, установив -n 1 на read, а затем проверить, просто ли вы прочитали "вверх" или "вниз" и используя history для получения необходимой команды. Вам, вероятно, придется придумать локальный var для подсчета "вверх" и "вниз", затем получить соответствующую команду из history с соответствующим смещением, вывести его на экран и, если следующий read будет возвращаться с пустую строку, используйте найденную команду.

Как я уже сказал, не могу проверить этот atm и не знаю, будет ли это работать.