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
Casing arrow keys in 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

