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