6

Very often I get some help information with the --help flag of a command, which gives the output at the terminal like:

$ vmtkimagereader --help

Creating vmtkImageReader instance.
Automatic piping vmtkimagereader
Parsing options vmtkimagereader

vmtkimagereader : read an image and stores it in a vtkImageData object
  Input arguments:
   -id Id (str,1); default=0: script id
   -handle Self (self,1): handle to self
   -disabled Disabled (bool,1); default=0: disable execution and
     piping

I want to syntax highlight the output like the upper half of the link (sorry that I can only post 1 link). I have tried highlight and pygmentize. However, highlight needs to specify a syntax, and pygmentize rendered the output as a wrong style (in the lower half of the link).

I'd like to know if there is a method to make the syntax highlight like this. Do I need to specify a style for pygmentize? Or do I have to turn to another solution?

Thanks!

Nick Bull
  • 9,518
  • 6
  • 36
  • 58
purplezzh
  • 97
  • 2
  • 9
  • It seems css rendered the paragraph quite well... but how to do it in a terminal? – purplezzh Aug 13 '16 at 09:06
  • 1
    You have 2 options (both very simple), you can either use ANSI escape sequences to color the text, or you can use `tput` to accomplish the same thing. This presumes you know what colors you want each part of the text to be so you can set the color at the appropriate time and reset to normal text when done. e.g. see [**Ansi Color Sequences**](https://en.wikipedia.org/wiki/ANSI_escape_code) – David C. Rankin Aug 13 '16 at 09:11
  • Thank you @DavidC.Rankin . ANSI escape is really fantastic. – purplezzh Aug 15 '16 at 08:13

2 Answers2

4

ANSI escape strings

Using ANSI escape sequences to achieve what you want, you can create a format string (represented by prepended \e[ and appended m) where 38;5;{0..255} is the 256-color of the text (0..255 being the range of available color codes), and 48;5;{0..255} is the 256-color of background. E.g.,

echo -e "\e[38;5;0;48;5;255mText\e[0m"

will print black text (color code 0) with a white background (color code 255). Note with the echo command it requires the extended mode (toggled by the -e flag) to interpret the ANSI escape string.

Note the trailing \e[0m to unset the coloring, otherwise all text printed after this command with echo will retain the format. \e[0m resets it.

Note an interesting use case that causes an error also. Placing an exclamation point before the ending \e[0m causes this output:

nick@nick-lt:~$ echo -e "\e[38;5;0;48;5;255mText!\e[0m"
bash: !\e[0m: event not found

That's because ! is part of string expansion for Bash. See more on this SO question here. To make that work as expected we need to do:

echo -e "\e[38;5;0;48;5;255mText"'!'"\e[0m"

as single-quotes do not get expanded.


How to print every available color using ANSI escape sequences.

Save these in a file called color-functions.sh:

function color_list_text() {
    # First paramter can be an optional background color
    local BGCOLOR="$1"

    COLOR=
    # Loop through the number range 0 to 255
    for COLOR in {0..255}; do
        local BGCOLORFORM=""
        # If our first parameter has a value, then create a background
        # format in ANSI escape sequence, assign to $BGCOLORFORM
        [[ -z "$BGCOLOR" ]] && BGCOLORFORM="48;5;${BGCOLOR};"

        # Create the whole ANSI escape sequence, assign to $TEXTFORM
        local TEXTFORM="${BGCOLORFORM}38;5;${COLOR}m"

        echo -en "\e[${TEXTFORM} ${COLOR}\t\e[0m"

        [[ $(( (COLOR + 1) % 16 )) -eq 0 ]] && echo
    done

    return 0
}

function color_list_text_backgrounds() {
    local TEXTCOLOR="$1"

    local COLOR
    for COLOR in {0..255}; do
        local TEXTCOLORFORM=""
        [[ -z "$TEXTCOLOR" ]] && TEXTCOLORFORM="38;5;${TEXTCOLOR};"

        local TEXTFORM="${TEXTCOLORFORM}48;5;${COLOR}m"

        echo -en "\e[${TEXTFORM} ${COLOR}\t\e[0m"

        [[ $(( (COLOR + 1) % 16 )) -eq 0 ]] && echo
    done

    return 0
}

Then, in another file, call the functions after you've source'd them:

source color-functions.sh

# Loops through and prints all ANSI escape sequence's available text colors
color_list_text
# Loops through and prints all ANSI escape sequence's available text backgrounds
color_list_backgrounds

Here's a function that does both... But I think it's overkill because the output is far too large (256 * 256 = 2^16 combinations will be outputted):

function color_list_text_and_backgrounds() {
    local BG
    for BG in {0..255}; do
        local TEXT
        for TEXT in {0..255}; do
            echo -en "\e[38;5;${TEXT};48;5;${BG}m ${TEXT}\t\e[0m"

            if [[ $(( (TEXT + 1) % 8 )) -eq 0 ]]; then
                if [[ $(( ((TEXT + 1) / 8) )) -eq 16 ]]; then
                    local INVBG=$(( (BG + 128) % 256 ))
                    echo -en "\e[38;5;${INVBG};48;5;${BG}m ${BG}\t\e[0m"
                else
                    echo -en "\e[48;5;${BG}m\t\e[0m"
                fi

                echo
            fi
        done
    done

    return 0
}

Your use case

To color certain things certain colors, we can use egrep -i (-i flag is case-insensitive) and the GREP_COLOR variable:

echo "Some string to color" | \
    GREP_COLOR='38;5;200' egrep -i --color=always 'some' | \
    GREP_COLOR='38;5;100' egrep -i --color=always 'string|color'

Or we could be real clever and functionise this:

color_text_match() {
    MATCHSTRING="$1"
    COLOR="$2"

    [[ -z "$MATCHSTRING" ]] && echo "color_text_match: No color specifies." 
    [[ -z "$COLOR" ]] && COLOR="214"   # Default orange

    GREP_COLOR="38;5;$COLOR" egrep -i --color=always "$MATCHSTRING"
}

Then:

echo "Some string to color" | \
    color_text_match "Some" | \
    color_text_match "string" | \
    color_text_match "to" "226"
Community
  • 1
  • 1
Nick Bull
  • 9,518
  • 6
  • 36
  • 58
0

Thank you, @NickBull. Your introduction to ANSI escape is a pretty good tutorial for newbies like me :-) I have tried all your code and made my color_help.sh to render the output:

function color_help() {
    COLOR_NUM="5"
    COLOR_CAP_WORD="4"
    COLOR_KEY_WORD="2"

    GREP_COLOR="38;5;$COLOR_NUM" egrep -E --color=always "\b[0-9]+(\.[0-9]+)*\b|$" | \
    GREP_COLOR="38;5;$COLOR_CAP_WORD" egrep -E --color=always "\b([A-Z][a-z]+)+\b|$" | \
    GREP_COLOR="38;5;$COLOR_KEY_WORD" egrep -E --color=always "\b(and|bool|default|float|for|from|int|self|str)\b|$"
}
purplezzh
  • 97
  • 2
  • 9