I'm waiting for user input (using 'read') in an infinite loop and would like to have command history, that is being able to show previous inputs that were already entered, using the up and down arrow keys, instead of getting ^[[A and ^[[B. Is this possible?
Thanks to @l0b0 for your answer. It got me on the right direction. After playing with it for some time I've realized I also need the following two features, but I haven't managed to get them yet:
If I press up and add something to the previous command I would like to have the whole thing saved in the history, not just the addition. Example
$ ./up_and_down
Enter command: hello
ENTER
Enter command:
Up
Enter command: hello you
ENTER
Enter command:
Up
Enter command: you
(instead of "hello you")
If I can't keep going up because I'm at the end of the history array, I don't want the cursor to move to the previous line, instead I want it to stay fixed.
This is what I have so far (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
The way to scroll using the arrow keys in a terminal emulator is to hold down the shift key. This is because a terminal emulator send all keypresses to the application running in the terminal; holding shift down bypasses that. This can also be used with other keys like copy and paste.
To detect the arrow key when it is pressed, use onkeydown in JavaScript. The button has key code. As you know the left arrow key has the code 37. The up arrow key has the code 38 and right has the 39 and down has 40.
Two solutions using the -e
option to the read
command combined with the builtin history
command:
# 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With