Bash – Is it possible to use ANSI color escape codes in Bash here-documents

bashcolorsescape-characters

I'm printing a message in a Bash script, and I want to colourise a portion of it; for example,

#!/bin/bash

normal='\e[0m'
yellow='\e[33m'
cat <<- EOF
    ${yellow}Warning:${normal} This script repo is currently located in:
    [ more messages... ]
EOF

But when I run in the terminal (tmux inside gnome-terminal) the ANSI escape characters are just printed in \ form; for example,

\e[33mWarning\e[0m This scr....

If I move the portion I want to colourise into a printf command outside the here-doc, it works.  For example, this works:

printf "${yellow}Warning:${normal}"
cat <<- EOF
    This script repo is currently located in:
    [ more messages... ]
EOF

From man bash – Here Documents:

No parameter and variable expansion, command substitution, arithmetic expansion, or pathname expansion is performed on word. If any characters in word are quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion.  In the latter case, the character sequence \<newline> is ignored, and \ must be used to quote the characters \, $, and `.

I can't work out how this would affect ANSI escape codes. Is it possible to use ANSI escape codes in a Bash here document that is catted out?

Best Answer

In your script, these assignments

normal='\e[0m'
yellow='\e[33m'

put those characters literally into the variables, i.e., \e[0m, rather than the escape sequence. You can construct an escape character using printf (or some versions of echo), e.g.,

normal=$(printf '\033[0m')
yellow=$(printf '\033[33m')

but you would do much better to use tput, as this will work for any correctly set up terminal:

normal=$(tput sgr0)
yellow=$(tput setaf 3)

Looking at your example, it seems that the version of printf you are using treats \e as the escape character (which may work on your system, but is not generally portable to other systems). To see this, try

yellow='\e[33m'
printf 'Yellow:%s\n' $yellow

and you would see the literal characters:

Yellow:\e[33m

rather than the escape sequence. Putting those in the printf format tells printf to interpret them (if it can).

Further reading:

Related Question