46

I often use something like read -e -p "> All good ? (y/n)" -n 1 confirm; to ask a confirm to the user.

I'm looking for a way to colorize the output, as the command echo -e does :

echo -e "\033[31m";
echo "Foobar";       // will be displayed in red
echo -e "\033[00m";

I'm using xterm.

In man echo, it says :

-e enable interpretation of backslash escapes

Is there a way to do the same thing with the read command ? (nothing in the man page :( -r option doesn't work)

4wk_
  • 2,458
  • 3
  • 34
  • 46

6 Answers6

66

read won't process any special escapes in the argument to -p, so you need to specify them literally. bash's ANSI-quoted strings are useful for this:

read -p $'\e[31mFoobar\e[0m: ' foo

You should also be able to type a literal escape character with Control-v Escape, which will show up as ^[ in the terminal:

read -p '^[[31mFoobar^[[0m: ' foo
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 3
    Hey, `read -p $'\e[31mFoobar\e[0m: ' foo` works perfectly. Can you explain a little what does the `$` do ? Maybe a link, or something, so I can accept your answer ;) – 4wk_ Jul 29 '14 at 06:51
  • 2
    Check the man page, near the end of the section `QUOTING`. [This page](http://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting) is for `bash` 4.2, but this feature has been stable for a while, so it shouldn't be (much) different from whatever version you are using. – chepner Jul 29 '14 at 12:49
  • 1
    One last thing @chepner : is there a way to give to the `-p` a variable ? I mean, with the `-p $''` structure, we can't give a variable, right ? I would like something like `read -p $'\e[31mFoobar $bar \e[0m: ' foo` – 4wk_ Jul 30 '14 at 07:40
  • 17
    @4wk_: Break the string into components: `read -p $'\e[31m'"$bar"$'\e[0m: ' foo`, and insert your variable in the middle. – Beggarman Jul 30 '14 at 16:23
  • @chepner : Perfect. But I guess if `$red` contains the string `\e[31m` and `$nocolor` contains `\e[0m`, I still can't cast them with the `$''` syntax, am I right ? – 4wk_ Jul 31 '14 at 07:42
  • 6
    Right; it would be nice if `$"..."` worked the same, but with parameter expansion, but that actually does something different. But you can use `$'...'` with variable assignments: `red=$'\e[31m'; nocolor=$'\e[0m'; read -p "${red}Foobar${nocolor}: "` – chepner Jul 31 '14 at 12:59
  • Relevant Bash docs are herE: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html – brandonsimpkins Aug 14 '18 at 23:16
  • It still prints as `^[[31mFoobar^[[0m:` in `zsh`, would it be possible to overcome this? – alper Nov 28 '21 at 12:34
56

Here's another solution that allows to use variables to change the text's format. echo -e the wanted output into the -p argument of the read command.

Example:

RESET="\033[0m"
BOLD="\033[1m"
YELLOW="\033[38;5;11m"
read -p "$(echo -e $BOLD$YELLOW"foo bar "$RESET)" INPUT_VARIABLE
Vrakfall
  • 966
  • 7
  • 13
22

Break your query into two components:

  1. use echo -e -n to display the prompt
  2. collect the user response with read

e.g:

echo -e -n "\e[0;31mAll good (y/n)? "   # Display prompt in red
echo -e -n '\e[0;0m'                    # Turn off coloured output
read                                    # Collect the user input

The echo -n option suppresses the trailing newline.

Beggarman
  • 866
  • 5
  • 7
8

this work for me :

    BC=$'\e[4m'
    EC=$'\e[0m'

    while true; do
            read -p "Do you wish to copy table from ${BC}$HOST $PORT${EC} to ${BC}$LOCAL_HOST $LOCAL_PORT${EC}? (y or n)" yn
        case $yn in
        ....
    done

Results are as follows: enter image description here

more example ,see the show case ,link is :

mysqlis

LuciferJack
  • 781
  • 11
  • 12
1

WATCH OUT, the answers here are correct with the given question, i.e limiting the number of read chars to 1 (-n 1) , as soon as one want to read more (i.e read a line) the answers here are not completely correct).

read -e -p "> All good ? (y/n)" -n 1

That is reading only 1 char (-n 1) and answers here are correct.

If one come to here with a similar question but with longer input (no -n1) then all the answers are wrong, as the long input will be screwed up by readline at the line wrap point.

As explained in the answers here:

P="\e[31mPrompt\e[m" ; read -e -p "$P" b

is not good as bash readline don't interpret the \e chars it just display them 'as is'


But:

P=$'\e[31mPrompt\e[m' ; read -e -p "$P" b

is better because the binary string is displayed by readline, but the prompt length is 7 chars longer than visible (the esc sequences) then making readline screwing up the line wrap on long input line, rendering wrongly the editing on the long line.

I found a way to trick readline and have prompt with attribute and yet handle long input. The idea is to emit the prompt with echo (as some have suggested) that will honor the esc sequence attributes and then do a readline with a prompt that has the visual length of the prompt string (PromptLen - escSeqLen) of the attributed echo but actually display nothing, that way readline emit the NOP prompt with correct length, and then handle long line correctly. I come up with this.

P="Prompt :"
echo -ne "\e[31m$P\e[m" ; read -e -p "${P//?/$'\a'}" b
echo "b='$b'"

This works, long line wrap is correctly handled, the drawback now is 'may be' an annoying BELL char, but most of the terminal honor the DECSMBV escape sequence to switch it off.

4wk_
  • 2,458
  • 3
  • 34
  • 46
Phi
  • 735
  • 7
  • 22
1

The issue with using terminal escape sequences with read -ep is that it messes up the handling of line wrapping by the prompt mechanism, because its assumption about the character width of the prompt ends up incorrectly counting the color codes.

The solution is to use special escape sequences \001 and \002 (otherwise known as RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE) to exclude the counting of non-printing characters, as shown in this example.

So, for example:

_YELLOW=$'\001\e[33m\002'
_RESET=$'\001\e[m\002'

read -erp "$_YELLOWprompt>$_RESET " input
echo
echo "Your input: $input"

Here is a working implementation.

webninja
  • 118
  • 1
  • 7