Bash shell arrow keys

Can I use the arrow keys in a bash script to run a specific set of commands when I press the up / left arrow keys and a specific set when I press the down / right arrow keys? I am trying to find a way to quickly switch between users using the arrow keys while displaying data, using this script to read data.

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}''-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.sh - full script.

The read command, commented as "waiting for user input", is the command for which you press enter to go to the next user. Essentially, this scenario lists the users and the time elapsed since each user logged in. I am trying to switch between the displayed user using the arrow keys. I thought that you could use a case statement to enter a key. To confirm my point, I'm not sure if this is possible. If not, can anyone come up with another way to do this?

+5
source share
6 answers

You can use read -n 1 to read a single character, then use the case to select an action to be performed based on the key.

In that the arrow keys display 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 displays ^[[C You can see the output sequence of your terminal by pressing Ctrl - V Right Arrow . The same is true for other cursor keys, such as Page Up and End .

Instead, I would recommend using single-character keys such as < and > . Processing them in your script will be much easier.

 read -n 1 key case "$key" in '<') go_left;; '>') go_right;; esac 
+6
source

As mentioned earlier, cursor keys generate three bytes - and keys such as home / end even generate four! The solution I saw somewhere was to allow the initial type <char read () to follow the three subsequent one-page char with a very short timeout. The most commonly used key sequences can be shown as follows:

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

In addition, / etc / inputrc contains some of them with readline mapping. So, answering the original question, here edit this bash menu, I just hack:

 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 
+14
source
 # 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 
+8
source

I'm not sure if this is the answer to the question directly, but I think it is connected - I wandered where these codes came from, and I finally found:

It is a little hard to read at first for the left arrow, find β€œLEFT 4” in the β€œKey” column, and for the sequence that bash sees, find the fifth column β€œkeymap” - β€œnormal”), where it is written as β€œ[D 1b 5b 44” is three bytes ( 27, 91, 68) representing this key.

Searching for a stream How to read arrow keys on really old bash? - The UNIX and Linux forums inspired me to write a short single-line liner that resets key codes for keystrokes. Basically, you press the key, then Enter (to start the end of read ), and then use hexdump to display what read has saved (and finally press Ctrl-C to exit the loop):

 $ 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 the arrow keys require 3 bytes - the Shift + arrow keys require 6! However, it would seem that all these sequences begin with 0x1b (27), so you could check this value for read -n1 before reading more bytes; also 5b remains the second byte in the multibyte sequence for the "normal" and "shift / NUM-Lock" columns in the table above.


Edit: it is much easier and more correct to check terminal codes of keystrokes in Linux via 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 
+6
source

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

 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 
+6
source

Using the answer of eMPee584 I think eMPee584 good solution for you. Its output largely coincides with the answer of user3229933 but will not be called by the Shift keys and will work in most terminals.

It has UP DOWN LEFT RIGHT HOUSES and END Keys Press q to exit Most of this thanks to eMPee584

you may need to change '-sn1' to '-sn1' if you get an error message like 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 
0
source

Source: https://habr.com/ru/post/1413462/


All Articles