bashscript для определения нажатия клавиши со стрелкой вправо

9

Почему это всегда определяется как истина, даже если код клавиши не является клавишей со стрелкой вправо?

stty_state=`stty -g`
stty raw; stty -echo
keycode=`dd bs=1 count=1 2>/dev/null`
stty "$stty_state"  

echo $keycode

if [ "$keycode"=39 ]; then
echo "Right Arrow Key Pressed!"
fi
ConfusedStack
источник
Связанный stackoverflow.com/questions/22842896/…
Сиро Сантилли 冠状 病毒 审查 六四 事件 法轮功

Ответы:

16

Вы (вероятно) читаете сначала из двух + байтов. $keycodeв вашем скрипте будет ESC при нажатии клавиши со стрелкой.

Клавиши со стрелками могут быть:

\x1b + some value

Он всегда оценивается как true из-за отсутствия пробелов в условном выражении.

Изменить: обновление этого заявления.

Ваш ifдействует на выходе из [команды. Команда [эквивалентна test. Тот факт, что это команда, является очень важным фактом. В качестве команды требуются пробелы между аргументами. Кроме того, [команда особенная, поскольку она требует ]последнего аргумента.

[ EXPRESSION ]

Команда выходит со статусом, определенным EXPRESSION. 1 или 0, правда или ложь .

Это не экзотический способ написать круглые скобки. Другими словами, он не является частью ifсинтаксиса, как, например, в C:

if (x == 39)

По:

if [ "$keycode"=39 ]; then

вы выпускаете:

[ "$keycode"=39 ]

который расширяется до

[ \x1b=39 ]

здесь \x1b=39читается как один аргумент. Когда testили [дается один аргумент, он завершается со значением false, только если EXPRESSION имеет значение null, чего никогда не будет. Даже если бы он $keycodeбыл пустым, это привело бы к =39(который не является нулевым / пустым).

Другой способ взглянуть на это так: вы говорите:

if 0 ; then # When _command_ exit with 0.
if 1 ; then # When _command_ exit with 1.

Прочитайте эти вопросы и ответы для получения более подробной информации - а также обсуждение [vs [[:

В связи с этим вы также можете исследовать тики назад `` против $( )


Многобайтовая escape-последовательность с клавишами со стрелками:

Как упомянуто сверху: вы (вероятно) читаете сначала из двух + байтов. $keycodeв вашем скрипте будет ESC при нажатии клавиши со стрелкой.

Стрелка и другие специальные клавиши приводят к тому, что в систему отправляются escape-последовательности . В ESC байтовые сигналы , которые «здесь приходит несколько байтов , которые должны быть истолкованы по- разному» . Что касается клавиш со стрелками , которые будут ASCII - [следуют ASCII A, B, Cили D.

Другими словами, вы должны анализировать три байта при работе с клавишами со стрелками.

Вы можете попробовать что-то в этом направлении, чтобы проверить:

{   stty_state=$(stty -g)
    stty raw isig -echo
    keycode=$(dd bs=8 conv=sync count=1)
    stty "$stty_state"
} </dev/tty 2>/dev/null
printf %s "$keycode" | xxd

Уступать:

HEX        ASCII
1b 5b 41   .[A # Up arrow
1b 5b 42   .[B # Down arrow
1b 5b 43   .[C # Right arrow
1b 5b 44   .[D # Left arrow
 |  |  |
 |  |  +------ ASCII A, B, C and D
 |  +--------- ASCII [
 +------------ ASCII ESC

Не уверен, насколько это переносимо, но раньше играл с таким кодом для ловли клавиш со стрелками. Нажмите, qчтобы выйти:

while read -rsn1 ui; do
    case "$ui" in
    $'\x1b')    # Handle ESC sequence.
        # Flush read. We account for sequences for Fx keys as
        # well. 6 should suffice far more then enough.
        read -rsn1 -t 0.1 tmp
        if [[ "$tmp" == "[" ]]; then
            read -rsn1 -t 0.1 tmp
            case "$tmp" in
            "A") printf "Up\n";;
            "B") printf "Down\n";;
            "C") printf "Right\n";;
            "D") printf "Left\n";;
            esac
        fi
        # Flush "stdin" with 0.1  sec timeout.
        read -rsn5 -t 0.1
        ;;
    # Other one byte (char) cases. Here only quit.
    q) break;;
    esac
done

(Как второстепенное примечание вы также (намереваетесь) проверить десятичную 39 - которая выглядит как смешивание десятичной и шестнадцатеричной. Первый байт в escape-последовательности - это значение ASCII ESC , которое является десятичным 27 и шестнадцатеричным 0x1b, а десятичное 39 - шестнадцатеричным 0x27. )

user367890
источник
2
Первая проблема в вопросе заключается в том, что =в тесте нет пробелов, окружающих знак, поэтому он анализируется просто как непустая строка и, следовательно, имеет значение true. Тот факт, что клавиши со стрелками состоят из нескольких байтов, является отдельной проблемой.
wurtel
2
Хм, это предложение не имеет большого контекста как есть, я перечитал его, поскольку думал, что это было частью объяснения многобайтовых последовательностей, которые я уже знал.
wurtel
2
@wurtel: Да. Я думаю, смутно. Выясните, что однажды объяснение [- это встроенная команда, люди понимают, почему пробелы важны гораздо быстрее. (Это не просто странный способ использования bash вместо скобок). Обновите один раз назад.
user367890