bash 中的大小写箭头键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10679188/
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 02:20:27  来源:igfitidea点击:

Casing arrow keys in bash

bash

提问by Jonathan Bondhus

Is it possible to case arrow keys in a bash script to run a certain set of commands if the up/left arrow keys are pressed, and a certain set if the down/right arrow keys are pressed? I am trying to have a way to switch between users quickly by using the arrow keys while it is displaying the data, using this script to read the data from.

如果按下向上/向左箭头键,是否可以在 bash 脚本中使用大小写箭头键来运行一组特定的命令,如果按下向下/向右箭头键,则可以运行特定的一组命令?我试图通过在显示数据时使用箭头键在用户之间快速切换,使用此脚本从中读取数据。

function main()  # The main function that controls the execution of all other functions
{
  mkdir -p ~/usertmp  # Make a new temporary user directory if it doesn't exist
  touch ~/last_seen_output.txt  # Create the output file if it doesn't exist
  cat /dev/null > ~/last_seen_output.txt  # Make sure that the output file is empty
  gather  # Call the "gather" function
  total=$((`wc -l ~/usertmp/user_list.txt|awk '{print }'`-1))  # Calculate the total amount of lines and subtract 1 from the result
  echo Current Time: `date +%s` > ~/last_seen_output.txt  # Print the current time to the output file for later reference
  echo "" > ~/last_seen_output.txt  # Print a blank line to the output file
    if [ $log -eq 1 ]
      then
        # If it is enabled, then delete the old backups to prevent errors
        while [ $line_number -le $total ]
          do

            line_number=$((line_number+1))  # Add 1 to the current line number
            calculate # Call the "calculate" function
            hms  # Call the "hms" function to convert the time in seconds to normal time
            log
        done
      else
        while [ $line_number -le $total ]
          do
            line_number=$((line_number+1))  # Add 1 to the current line number
            calculate # Call the "calculate" function
            hms  # Call the "hms" function to convert the time in seconds to normal time
            echo "Displaying, please hit enter to view the users one by one."
            read  # Wait for user input
            if [ "$log_while_displaying" ]
              then
                log
                display
              else
                display
            fi
        done
    fi
}

https://github.com/jbondhus/last-seen/blob/master/last-seen.shis the complete script.

https://github.com/jbondhus/last-seen/blob/master/last-seen.sh是完整的脚本。

The read command commented as "wait for user input" is the command that you hit enter to go to the next user for. Basically, what this script does it list users and the time that has passed since each user logged in. I am trying to switch between each user being displayed by using the arrow keys. I figured that it might be possible to use a case statement to case the key input. To reiterate my point, I'm not sure if this is possible. If it isn't, can anyone think of another way to do this?

注释为“等待用户输入”的读取命令是您按回车键转到下一个用户的命令。基本上,这个脚本的作用是列出用户和每个用户登录后经过的时间。我试图使用箭头键在显示的每个用户之间切换。我认为可能可以使用 case 语句来区分键输入。重申我的观点,我不确定这是否可能。如果不是,有人能想出另一种方法来做到这一点吗?

回答by eMPee584

As mentioned before, the cursor keys generate three bytes - and keys like home/end even generate four! A solution I saw somewhere was to let the initial one-char read() follow three subsequent one-char reads with a very short timeout. Most common key sequences can be shown like this f.e.:

如前所述,光标键生成三个字节 - 而像 home/end 这样的键甚至生成四个!我在某处看到的一个解决方案是让最初的单字符 read() 跟随三个后续单字符读取,超时时间很短。最常见的键序列可以像这样显示:

#!/bin/bash
for term in vt100 linux screen xterm
  { echo "$term:"
    infocmp -L1 $term|egrep 'key_(left|right|up|down|home|end)'
  }

Also, /etc/inputrc contains some of these with readline mappings.. So, answering original question, here's a snip from that bash menu i'm just hacking away at:

另外,/etc/inputrc 包含其中一些带有 readline 映射的内容。所以,回答原始问题,这是我正在破解的那个 bash 菜单中的一个片段:

while read -sN1 key # 1 char (not delimiter), silent
do
  # catch multi-char special key sequences
  read -sN1 -t 0.0001 k1
  read -sN1 -t 0.0001 k2
  read -sN1 -t 0.0001 k3
  key+=${k1}${k2}${k3}

  case "$key" in
    i|j|$'\e[A'|$'\e0A'|$'\e[D'|$'\e0D')  # cursor up, left: previous item
      ((cur > 1)) && ((cur--));;

    k|l|$'\e[B'|$'\e0B'|$'\e[C'|$'\e0C')  # cursor down, right: next item
      ((cur < $#-1)) && ((cur++));;

    $'\e[1~'|$'\e0H'|$'\e[H')  # home: first item
      cur=0;;

    $'\e[4~'|$'\e0F'|$'\e[F')  # end: last item
      ((cur=$#-1));;

    ' ')  # space: mark/unmark item
      array_contains ${cur} "${sel[@]}" && \
      sel=($(array_remove $cur "${sel[@]}")) \
      || sel+=($cur);;

    q|'') # q, carriage return: quit
      echo "${sel[@]}" && return;;
  esac                  

  draw_menu $cur "${#sel[@]}" "${sel[@]}" "$@" >/dev/tty
  cursor_up $#
done

回答by user3229933

# This will bind the arrow keys

while true
do
    read -r -sn1 t
    case $t in
        A) echo up ;;
        B) echo down ;;
        C) echo right ;;
        D) echo left ;;
    esac
done

回答by JellicleCat

You can read arrow keys as well as other keys without any unusual commands; you just need two readcalls instead of one:

您可以阅读箭头键以及其他键,而无需任何异常命令;你只需要两个read电话而不是一个:

escape_char=$(printf "\u1b")
read -rsn1 mode # get 1 character
if [[ $mode == $escape_char ]]; then
    read -rsn2 mode # read 2 more chars
fi
case $mode in
    'q') echo QUITTING ; exit ;;
    '[A') echo UP ;;
    '[B') echo DN ;;
    '[D') echo LEFT ;;
    '[C') echo RIGHT ;;
    *) >&2 echo 'ERR bad input'; return ;;
esac

回答by Paused until further notice.

You can use read -n 1to read one character then use a casestatement to choose an action to take based on the key.

您可以使用read -n 1读取一个字符,然后使用case语句根据键选择要执行的操作。

On problem is that arrow keys output more than one character and the sequence (and its length) varies from terminal to terminal.

问题是箭头键输出一个以上的字符,并且序列(及其长度)因终端而异。

For example, on the terminal I'm using, the right arrow outputs ^[[C. You can see what sequence your terminal outputs by pressing Ctrl-VRight Arrow. The same is true for other cursor-control keys such as Page Upand End.

例如,在我使用的终端上,右箭头输出^[[C. 您可以通过按Ctrl-来查看终端输出的顺序VRight Arrow。其他光标控制键(例如Page Up和 )也是如此End

I would recommend, instead, to use single-character keys like <and >. Handling them in your script will be much simpler.

相反,我建议使用单字符键,如<>。在脚本中处理它们会简单得多。

read -n 1 key

case "$key" in
    '<') go_left;;
    '>') go_right;;
esac

回答by sdaau

Not sure if this answer the question directly, but I think it's related - I was wandering where do those codes come from, and I finally found:

不确定这是否直接回答了问题,但我认为这是相关的 - 我在徘徊这些代码来自哪里,我终于找到了:

It's a bit difficult to read at first; for left arrow, lookup "LEFT 4" in the "Key" column, and for the sequence that bashsees, look up the 5th ("keymap" - "normal") column, where it is written as "[D 1b 5b 44" - which are the three bytes (27, 91, 68) representing this key.

一开始读起来有点困难;对于左箭头,在“Key”列中查找“LEFT 4”,对于bash看到的序列,查找第 5 个(“keymap”-“normal”)列,它被写为“[D 1b 5b 44” - 代表这个键的三个字节(27、91、68)。

Finding the thread How to read arrow keys on really old bash? - The UNIX and Linux Forums, inspired me to write a short one-liner which dumps the key codes of keys pressed. Basically, you press a key, then Enter (to trigger ending of read), and then use hexdumpto output what readhas saved (and finally hit Ctrl-C to exit the loop):

查找线程如何在非常旧的 bash 上阅读箭头键?- UNIX 和 Linux 论坛,启发我写了一个简短的单行代码,它转储按键的键码。基本上,您按下一个键,然后按 Enter(触发 结束read),然后使用hexdump输出read已保存的内容(最后按 Ctrl-C 退出循环):

$ while true; do read -p?; echo -n $REPLY | hexdump -C; done
?^[[D     
00000000  1b 5b 44                                          |.[D| # left arrow
00000003
?^[[C
00000000  1b 5b 43                                          |.[C| # right arrow
00000003
?^[[1;2D
00000000  1b 5b 31 3b 32 44                                 |.[1;2D| # Shift+left arrow
00000006
?^[[1;2C
00000000  1b 5b 31 3b 32 43                                 |.[1;2C| # Shift+right arrow
00000006
?^C

So, while arrow keys require 3 bytes - Shift+arrow keys require 6! However, seemingly all these sequence start with 0x1b (27), so one could possibly check for this value for read -n1, before reading any more bytes; also 5bremains a second byte in multi-byte sequence for the "normal" and "shift/NUM-Lock" columns of the table above.

因此,虽然箭头键需要 3 个字节 - Shift+箭头键需要 6 个!然而,似乎所有这些序列都以 0x1b (27) 开头,因此read -n1在读取更多字节之前,可以检查 , 的这个值;还5b为上表的“正常”和“移位/NUM-Lock”列保留了多字节序列中的第二个字节。



Edit: much easier and proper way to scan for terminal codes of pressed keys in Linux is via showkey:

编辑:在 Linux 中扫描按键终端代码的更简单和正确的方法是通过showkey

$ showkey 
Couldn't get a file descriptor referring to the console

$ showkey -h
showkey version 1.15

usage: showkey [options...]

valid options are:

    -h --help   display this help text
    -a --ascii  display the decimal/octal/hex values of the keys
    -s --scancodes  display only the raw scan-codes
    -k --keycodes   display only the interpreted keycodes (default)

$ sudo showkey -a

Press any keys - Ctrl-D will terminate this program

^[[A     27 0033 0x1b
         91 0133 0x5b
         65 0101 0x41
^[[B     27 0033 0x1b
         91 0133 0x5b
         66 0102 0x42
^[[A     27 0033 0x1b
         91 0133 0x5b
         65 0101 0x41
^[[D     27 0033 0x1b
         91 0133 0x5b
         68 0104 0x44
^[[C     27 0033 0x1b
         91 0133 0x5b
         67 0103 0x43
^C       3 0003 0x03
^M       13 0015 0x0d
^D       4 0004 0x04

回答by djorborn

Using eMPee584answer I think I came up a good solution for you. Its output is much the same as user3229933answer but will not be triggered by shift keys and will work in most terminals.

使用eMPee584答案,我想我为您提出了一个很好的解决方案。它的输出与user3229933answer非常相似,但不会被 shift 键触发,并且可以在大多数终端中使用。

It has UP DOWN LEFT RIGHT HOME and END Keys Press 'q' to quit Most of this is thanks to eMPee584

它有 UP DOWN LEFT RIGHT HOME 和 END 键按“q”退出大部分这要归功于 eMPee584

you may need to change '-sn1' to '-sN1' if you get an error like illegal option n.

如果出现类似illegal option n.

#!/bin/bash

while read -sn1 key # 1 char (not delimiter), silent
do

  read -sn1 -t 0.0001 k1 # This grabs all three symbols 
  read -sn1 -t 0.0001 k2 # and puts them together
  read -sn1 -t 0.0001 k3 # so you can case their entire input.

   key+=${k1}${k2}${k3} 

  case "$key" in
    $'\e[A'|$'\e0A')  # up arrow
        ((cur > 1)) && ((cur--))
        echo up;;

    $'\e[D'|$'\e0D') # left arrow
        ((cur > 1)) && ((cur--))
        echo left;;

    $'\e[B'|$'\e0B')  # down arrow
        ((cur < $#-1)) && ((cur++))
        echo down;;

    $'\e[C'|$'\e0C')  # right arrow
        ((cur < $#-1)) && ((cur++))
        echo right;;

    $'\e[1~'|$'\e0H'|$'\e[H')  # home key:
        cur=0
        echo home;;

    $'\e[4~'|$'\e0F'|$'\e[F')  # end key:
        ((cur=$#-1))
        echo end;;

    q) # q: quit
        echo Bye!
        exit;;

   esac                  

done

回答by Paul Hedderly

To extend JellicleCat's answer:

扩展 JellicleCat 的回答:

#!/bin/bash
escape_char=$(printf "\u1b")
read -rsn1 mode # get 1 character
if [[ $mode == $escape_char ]]; then
    read -rsn4 -t 0.001 mode # read 2 more chars
fi
case $mode in
    '') echo escape ;;
    '[a') echo UP ;;
    '[b') echo DOWN ;;
    '[d') echo LEFT ;;
    '[c') echo RIGHT ;;
    '[A') echo up ;;
    '[B') echo down ;;
    '[D') echo left ;;
    '[C') echo right ;;
    '[2~') echo insert ;;
    '[7~') echo home ;;
    '[7$') echo HOME ;;
    '[8~') echo end ;;
    '[8$') echo END ;;
    '[3~') echo delete ;;
    '[3$') echo DELETE ;;
    '[11~') echo F1 ;;
    '[12~') echo F2 ;;
    '[13~') echo F3 ;;
    '[14~') echo F4 ;;
    '[15~') echo F5 ;;
    '[16~') echo Fx ;;
    '[17~') echo F6 ;;
    '[18~') echo F7 ;;
    '[19~') echo F8 ;;
    '[20~') echo F9 ;;
    '[21~') echo F10 ;;
    '[22~') echo Fy ;;
    '[23~') echo F11 ;;
    '[24~') echo F12 ;;
    '') echo backspace ;;
    *) echo $mode;;
esac