Using color in scripts

This is supposed to colorize.

But it outputs this

3[0;32mFile exists.3[0m

GREEN='3[0;32m'
RED='3[0;31m'
WHITE='3[0;37m'
RESET='3[0m'
FILE="/usr/share/sounds/My_Sounds/Short_doorbell.wav"

if [ -f "$FILE" ];
then
     echo -e "${GREEN}File exists.${RESET}"
else
     echo -e "${RED}File does NOT exist.${RESET}"
     
fi

Hi drew77...

GREEN='\033[0;32m'
RED='\033[0;31m'
WHITE='\033[0;37m'
RESET='\033[0m'
2 Likes

Expanding a little on what wisecracker has already said....

It looks like you are trying to use ANSI terminal escape codes, but they start with an <escape> character; not with the character <3>. Furthermore, you haven't told us what shell you're using and the behavior of echo -e varies considerably from shell to shell, from operating system to operating system, and even if you use the same shell on multiple systems, the behavior of echo -e may vary even when echo is a built-in utility within a given shell.

Using printf avoids the various behaviors of echo and the changes to your color introducing variables can be fixed by replacing the leading 3 with \033 (i.e., the ASCII escape character). In bash you can replace the \033 with \e . In ksh93 you can replace the \033 with either \e or \E . These uses of \e and \E can be used to reference the <escape> character even when you're using those shells on a mainframe that uses EBCDIC instead of an ASCII based codeset. (But you probably won't find terminals that accept ANSI terminal escape codes on a mainframe.)

Note also that it is usually considered to be bad form to write these escape sequences into a file unless the file is associated with a terminal device. For example, on systems where you can tell the ls utility to use various colors to indicate various types of files, it only does that when standard output is directed to a terminal device file. If you redirect the output of ls to a regular file, it won't output the escape sequences to produce those colors.

GREEN='\033[0;32m'
RED='\033[0;31m'
WHITE='\033[0;37m'
RESET='\033[0m'
FILE="/usr/share/sounds/My_Sounds/Short_doorbell.wav"

if [ -f "$FILE" ]
then
     printf "${GREEN}File exists.${RESET}\n"
else
     printf "${RED}File does NOT exist.${RESET}\n"  
fi

For other ANSI terminal escapes (such as bold, underlined, and/or crossed out text and cursor motion and blink rates see ANSI escapes codes in Wikipedia.

3 Likes

I am using mate terminal.

I found another way which is easier to use and tput is part of the ncurses package and is supplied with most Linux distributions.

FILE="/usr/share/sounds/My_Sounds/Short_doorbell.wav"

if [ -f "$FILE" ];
then
     tput setaf 2; echo "File $FILE does exist."
else
     tput setaf 1; echo "File $FILE does NOT exist."
     
fi
tput sgr0    # Reset text format to the terminal's default


#Num  Colour    #define         R G B

#0    black     COLOR_BLACK     0,0,0
#1    red       COLOR_RED       1,0,0
#2    green     COLOR_GREEN     0,1,0
#3    yellow    COLOR_YELLOW    1,1,0
#4    blue      COLOR_BLUE      0,0,1
#5    magenta   COLOR_MAGENTA   1,0,1
#6    cyan      COLOR_CYAN      0,1,1
#7    white     COLOR_WHITE     1,1,1
3 Likes

Hi.

Here is an example of hiding a lot of the code (like tput ) and allowing a pattern to be specified for conditional high-lighting:

#!/usr/bin/env bash

# @(#) s1       Demonstrate colorization of text.

# Utility functions: print-as-echo, print-line-with-visual-space, debug.
# export PATH="/usr/local/bin:/usr/bin:/bin"
LC_ALL=C ; LANG=C ; export LC_ALL LANG
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
em() { pe "$*" >&2 ; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }
C=$HOME/bin/context && [ -f $C ] && $C my-hilite

pl " Results, file missing:"
FILE=test1
rm -f $FILE
if [ -f "$FILE" ];
then
  echo "File $FILE does exist." |
  my-hilite -f green
else
  echo "File $FILE does NOT exist." |
  my-hilite -f red
fi

touch $FILE
pl " Results, file present:"
if [ -f "$FILE" ];
then
  echo "File $FILE does exist." |
  my-hilite -f green
else
  echo "File $FILE does NOT exist." |
  my-hilite -f red
fi

pl " Results, if only string \"File\" should be colored:"
echo " The File is unimportant, the contents are what matters." |
my-hilite -f blue "File"

exit 0

producing:

./s1 | ansifilter -B

Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 3.16.0-7-amd64, x86_64
Distribution        : Debian 8.11 (jessie) 
bash GNU bash 4.3.30
my-hilite - ( local: RepRev 1.2, ~/bin/my-hilite, 2019-04-11 )

-----
 Results, file missing:
File test1 does NOT exist.

-----
 Results, file present:
File test1 does exist.

-----
 Results, if only string "File" should be colored:
 The File is unimportant, the contents are what matters.

The ansifilter is so that the colors show up here, and would not be needed on a terminal -- I use Konsole, I'm assuming that Mate will be similar.

I'll try to attach the script my-hilite as a text file (in this or a following post) so that you may rename it, modify it, etc.

Best wishes ... cheers, drl

--- Post updated at 10:56 ---

Hi.

Cannot seem to get attached file to work, so copied here ... cheers, drl

#!/usr/bin/env bash

# @(#) my-hilite        Colorize regex pattern matches, insert tput codes with sed.
# $Id: my-hilite,v 1.2 2019/04/11 15:12:31 drl Exp $

# Home:
# http://unix.stackexchange.com/questions/46562/how-do-you-colorize-only-some-keywords-for-a-bash-script

# Utility functions: print-as-echo, print-line-with-visual-space, debug.
LC_ALL=C ; LANG=C ; export LC_ALL LANG
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
em() { pe "$*" >&2 ; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }

# Short circuit for debug -- MUST BE FIRST ARGUMENT.
if [[ $# -gt 0 && "$1" =~ ^-de*b*u*g* ]]
then
  db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
  shift;
else
  db() { : ; }
fi

color_to_num () {
  case $1 in
    black)  echo 0;;
    red)    echo 1;;
    green)  echo 2;;
    yellow) echo 3;;
    blue)   echo 4;;
    purple) echo 5;;
    cyan)   echo 6;;
    white)  echo 7;;
    *)      echo 0;;
  esac
}

# default values for foreground and background colors
bg=
fg=
bold="$(tput bold)"
italics=""
boundary=""

while getopts f:b:sli option; do
  case "$option" in
    f) fg="$OPTARG";;
    b) bg="$OPTARG";;
    s) bold="";;
    l) boundary=".*";;
    i) italics="$(tput sitm)";;
  esac
done

shift $(($OPTIND - 1))

if [ $# -le 0 ]
then
  pattern="^.*$"
else
  pattern="$*"
fi

if [ -n "$fg" ]; then
  fg=$(tput setaf $(color_to_num $fg))
fi
if [ -n "$bg" ]; then
  bg=$(tput setab $(color_to_num $bg))
fi

if [ -z "$fg$bg" ]; then
  fg=$(tput smso)
fi

db " boundary = :$boundary:"
db " pattern  = :$pattern:"
sed "s/${boundary}${pattern}${boundary}/${bold}${italics}${fg}${bg}&$(tput sgr0)/g"

exit

Some of these terminal escape codes even 'tput' can't do.
All work on CygWin's 'mintty' terminal too.

#!/bin/sh

# For those that do not have tput.
# Some of these cannot even be done using tput!
# All work on the majority of terminals but some terminals can't do them all.

# Resize the terminal on the fly, even works on CygWin's mintty, this resizes to 30 rows by 86 columns.
echo "Resize terminal to 30 x 86."
printf "%b" "\033[8;30;86t"
sleep 2
# And resize to a standard size of 24 rows by 80 columns.
echo "Resize terminal to 24 x 80."
printf "%b" "\033[8;24;80t"

# Reset terminal for machines that do not have it.
echo "Reset the terminal."
sleep 2
printf "%b" "\033c\033[0m\033[2J\033[H"

# Clear the screen.
echo "Clear the screen."
sleep 1
printf "%b" "\033[2J\033[H"
echo "Alternate clear screen."
sleep 1
# OR...
printf "%b" "\033[2J\033[1;1f"

# Clear the terminal current buffer only.
printf "%b" "\033[3J"

# Write into the terminal title bar.
printf "%b" "\033]0;Barry Walker.\007"
sleep 2
# And clear it again.
printf "%b" "\033]0;\007"

Enjoy lads and lassies...

1 Like

Something is missing in the code.

Hilite.sh

-----
 Results, file missing:
/home/andy/bin/Hilite.sh: line 24: my-hilite: command not found

-----
 Results, file present:
/home/andy/bin/Hilite.sh: line 32: my-hilite: command not found

-----
 Results, if only string "File" should be colored:
/home/andy/bin/Hilite.sh: line 40: my-hilite: command not found

What output do you get from the commands:

pwd
printf '%s\n' "$PATH"
ls -l my-hilite
type env bash

when run in the directory where you installed my-hilite ?

andy@7_~/bin$ pwd
/home/andy/bin
andy@7_~/bin$ printf '%s\n' "$PATH"
/home/andy/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
andy@7_~/bin$ ls -l my-hilite
ls: cannot access 'my-hilite': No such file or directory
andy@7_~/bin$ type env bash
env is /usr/bin/env
bash is /bin/bash

I named my script Hilite.sh if that makes a difference.

It makes a difference.

If someone gives you a script named my_hilite and you do not install it with the name my-hilite , then you need to change everything that references my-hilite to reference whatever named you used when you installed it instead.

And, when you install it you have to make sure that the script will be readable and executable by whatever users want to run that script.

chmod 755 Hilite.sh

should give you a reasonably readable, executable script. Then you just have to find EVERYTHING that attempts to invoke my-hilite and change every reference to it to be Hilite.sh .

Don...

Clerical error.

chmod 755 Hilite.sh not chmod 755 Hiliite.sh .

1 Like

Sorry, and thank you wisecracker. I have now fixed this blunder in post #10.

1 Like

I renamed it back to orig name.

my-hilite.sh

-----
 Results, file missing:
/home/andy/bin/my-hilite.sh: line 24: my-hilite: command not found

-----
 Results, file present:
/home/andy/bin/my-hilite.sh: line 32: my-hilite: command not found

-----
 Results, if only string "File" should be colored:
/home/andy/bin/my-hilite.sh: line 40: my-hilite: command not found

The scripts is relying on my-hilite which does not exist.

else
  echo "File $FILE does NOT exist." |
  my-hilite -f red

Hi drew77...

Of course it doesn't it is named my-hilite NOT my-hilite.sh .
Try mv my-hilite.sh my-hilite and retry...

Did so.

-----
 Results, file missing:

-----
 Results, file missing:

-----
 Results, file missing:

-----
 Results, file missing:

-----
 Results, file missing:

/home/andy/bin/my-hilite: fork: retry: Resource temporarily unavailable
/home/andy/bin/my-hilite: fork: retry: Resource temporarily unavailable
/home/andy/bin/my-hilite: fork: retry: Resource temporarily unavailable
/home/andy/bin/my-hilite: fork: retry: Resource temporarily unavailable
/home/andy/bin/my-hilite: fork: retry: Resource temporarily unavailable

And I have a runnaway script. :slight_smile:

I use the Mate-Terminal.

What terminal have you tested it on?

I had to change my-hilite to ./my-hilite as I ran both files from my current directory:

My terminal is the default Apple Macbook Pro OSX 10.14.3, a derivative of xterm IIRC.

I called the first file 'Colour_test.sh' and the second 'my-hilite' see attached image.
The code works perfectly.

EDIT:
It's late here.
Goodnight all...

I know you are catching some zzzs, so maybe someone else can solve a mystery.

I have no Colour_test.sh.

Where did that come from?

In post #5 in this thread drl gave you two scripts and showed you the output the first script produced when it was run after the second script had been installed under the name my-hilite .

It seems that wisecracker installed those two scripts on his system naming the first script Colour_test.sh and naming the second script my-hilite . When wisecracker ran Colour_test.sh it produced the output that drl showed us in post #5 and that wisecracker showed us again in post #16. I also installed those two scripts on my system with the same names that wisecracker used, and when I ran Colour_test.sh it also produced the output that drl showed us in post #5.

We are all having a hard time understanding how you installed two files named my-hilite in the same directory. But, if you had installed what we are calling Colour_test.sh in a file named my-hilite and invoked it with the name my-hilite that would indeed produce the output you showed us since that script was very busy calling itself until you exceeded the number of processes your operating system allowed to to run concurrently.

1 Like

I am thoroughly confused.

I will stick with my tput script.

It fulfills my needs and is not overly complex.

When you say "install" what exactly do you mean.

I understand it as

  1. Creating the script or scripts
  2. Making it executable