I have a Bash script that is being run interactively and as a cron job. When run interactively it outputs colored text on the terminal. However, when run in cron it doesn't have a terminal and therefore I get plenty of [1;31m and similar in the output.

Is there a way in Bash to tell it to swallow the escape codes if the output device doesn't support them?

This is related to How to check if bash can print colors which is about checking the condition. However, I want to change as little as possible in the invocation of echo or printf while retaining the dual functionality of colors or not depending on the output device.

3 Answers 3


Check whether you're printing to a terminal. That's what programs such as GNU ls and GNU grep do when you tell them --color=auto.

Even if you're printing to a terminal, in theory, it might not understand the color-changing escape sequences. In practice, all the common and most not-so-common terminals understand these sequences: all the X11 terminal emulators I've ever seen, screen, tmux, the Linux console, the *BSD consoles, PuTTY, rxvt, Console2, ConEmu, …

if [ -t 1 ]; then
 echo "foobar ${green}OK${normal}"

The test is [ -t 1 ] for standard output and [ -t 2 ] for standard error.

In shells that don't support $'…' expansion, you can generate an escape character portably with esc=$(echo _ | tr _ '\033')


Not exactly swallowing, but you can remove them with parameter expansion:

str='Hello \e[31mc\e[32mo\e[33ml\e[34mo\e[35mr\e[m world'

# colorful output
echo -e "$str"

# colorless output    
echo -e "${str//\\e\[+([0-9;])m}"

The above in bash requires the extglob shell option to be turned on. (shopt -s extglob)

To make it easier to use, define a function:

function ecco() { [ -t 1 ] && echo -e "$1" || echo -e "${1//\\e\[+([0-9;])m}"; }

Then you just call it:

ecco 'Hello \e[31mc\e[32mo\e[33ml\e[34mo\e[35mr\e[m world'

To check that it works, just redirect its output and the colors will disappear:

ecco 'Hello \e[31mc\e[32mo\e[33ml\e[34mo\e[35mr\e[m world' | cat
  • That's a very clever idea. Thanks. Accepting. Nov 19, 2013 at 20:01
  • 1
    You're missing the part where you determine whether to do the filtering. Nov 20, 2013 at 0:15

you should just check, in your script, if it's being run interactively or not?



6.3.2 Is this Shell Interactive?

To determine within a startup script whether or not Bash is running interactively, test the value of the ‘-’ special parameter. It contains i when the shell is interactive. For example:

case "$-" in
  *i*)   echo This shell is interactive ;;
   *)    echo This shell is not interactive ;;

Alternatively, startup scripts may examine the variable PS1; it is unset in non-interactive shells, and set in interactive shells. Thus:

if [ -z "$PS1" ]; then
   echo This shell is not interactive
   echo This shell is interactive

and define a set of variables to "_escape_codes_" if it's interactive and to "" if not, and use those variables to colorize the output in the script (in the interactive environment they will have escape codes, in the non-interactive they will add nothing to the text).


case "$-" in
  *i*)    _bold_="$(printf '\033[1m')"
          _norm_="$(printf '\033[0m')"
  *)      _bold_=""
echo "this ${_bold_}text${_norm_} is important"
  • 1
    note that I know the double quotes around my "$(...)" are not needed here, but I prefer to get in the habit when defining variables... and it colorizes "better" (ymmv) in some editors. Nov 19, 2013 at 19:52
  • 1
    Whether the shell is interactive is not the issue here. The script won't be run by an interactive shell, what matters is whether stdout (or stderr as the case may be) is a terminal. Nov 20, 2013 at 0:14
  • @Gilles: good thinking. you're right! Nov 20, 2013 at 9:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .