one-liner
one-liner

Reputation: 799

Bash script user input prompt

I am having issues with my known methods of generating user input prompts:

read -p "Input something: " variabile

This causes issues if attempting to use the arrow keys, it echoes the ANSI code for each arrow key stroke

read -e -p "Input something: " variable

This fixes the arrow keys issue but when reaching the width of the terminal, text input doesn't continue on a newline but on the same line, overwriting (visually) the existing input

echo -n "Input something: "; read -e variable

This apparently fixes both formerly described issues... until I found that typing something then hitting backspace overwrites the prompt and also when the input is longer, from the second newline of input the visual overwriting manifests again.

So is there a good way of producing prompts without the above issues?

UPDATE

After re-checking, I now know what's causing the input overwrite for read -e -p I am using these variables for highlighting text for the read prompt:

highlight=$(echo -e "\e[1;97m")
clear=$(echo -e "\e[0m")
read -e -p "Input$highlight something$clear: " variable

This is the only way I could make the highlighting work inside read prompt (assigning escape sequences to the variables doesn't work, I need to echo them like I did) but they also seem to cause the input overwrite issue.

Upvotes: 4

Views: 1611

Answers (2)

dimo414
dimo414

Reputation: 48824

The shell keeps track of how long it thinks the prompt is, in order to know where the user's input starts and stops. Unfortunately when you print color escape codes in a prompt you throw of Bash's counting, since it expects the escape characters to take up space in the terminal.

To avoid that, you just need to wrap all color sequences in \[ and \], which tells your shell the enclosed characters are non-printing, and should not be counted.

For example, your highlight variable should be:

highlight=$(echo -e "\[\e[1;97m\]")

Personally, I use the color and pcolor functions from my Prompt.gem project, which handles the proper escaping and would make your command much easier to read:

read -e -p "Input $(pcolor DEFAULT BOLD)something$(pcolor): " variable

Upvotes: 1

Robin like the bird
Robin like the bird

Reputation: 752

As dimo414 mentions, readline thinks the prompt is longer than it is. It counts every character in the terminal escape sequence in computing the length. You can see how long it thinks the escape sequence is as follows

echo ${#highlight}

In the bash PS1 prompt, surrounding such an escape sequence with "\[" and "\]" instructs readline to ignore everything between when calculating current line length, but these are not the right escapes for the bash read built-in.

The escapes for read are $'\001' and $'\002', as mentioned in BashFAQ, but in my experience, you need the -e option on read, as well. The brute force way to do what you want would be:

read -e -p "Input "$'\001'"${highlight}"$'\002'something$'\001'"${clear}"$'\002'": "

You should use tput rather than hard-coded escape sequences, for the sake of terminal independence. Read man 5 termcap.

See my dotfiles for elegant bash functions to do the begin/end quoting above for you.

Upvotes: 1

Related Questions