140

When written like this, it outputs text in blue:

printf "\e[1;34mThis is a blue text.\e[0m"

But I want to have format defined in printf:

printf '%-6s' "This is text"

Now I have tried several options how to add color, with no success:

printf '%-6s' "\e[1;34mThis is text\e[0m"

I even tried to add attribute code to format with no success. This does not work and I can't find anywhere an example, where colors are added to printf, which has defined format as in my case.

Aleksandar M
  • 125
  • 5
Jernej Jerin
  • 3,179
  • 9
  • 37
  • 53

9 Answers9

220

Rather than using archaic terminal codes, may I suggest the following alternative. Not only does it provide more readable code, but it also allows you to keep the color information separate from the format specifiers just as you originally intended.

blue=$(tput setaf 4)
normal=$(tput sgr0)

printf "%40s\n" "${blue}This text is blue${normal}"

See my answer HERE for additional colors

Community
  • 1
  • 1
SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • 3
    // , This also makes it so that I don't have to document what the codes mean. I think it'll be a step forward to helping our group see scripts as docs. – Nathan Basanese May 10 '17 at 20:55
  • Not only that, but it's portable to many more terminal types than hard-coding specific escape codes. – Toby Speight Oct 10 '21 at 09:10
98

You're mixing the parts together instead of separating them cleanly.

printf '\e[1;34m%-6s\e[m' "This is text"

Basically, put the fixed stuff in the format and the variable stuff in the parameters.

geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • 1
    Some extra details here: The command `printf` consists of a _format string_ and an _argument list_ which printed according to the format. You should see colours as part of the format-string and hence, they should belong in the format string. – kvantour Oct 23 '19 at 09:40
  • great answer, totally changed how I was looking at the problem – Zombo Dec 26 '20 at 02:09
54

This works for me:

printf "%b" "\e[1;34mThis is a blue text.\e[0m"

From printf(1):

%b     ARGUMENT as a string with '\' escapes interpreted, except that octal
       escapes are of the form \0 or \0NNN
Vlad
  • 35,022
  • 6
  • 77
  • 199
29

This is a small program to get different color on terminal.

#include <stdio.h>

#define KNRM  "\x1B[0m"
#define KRED  "\x1B[31m"
#define KGRN  "\x1B[32m"
#define KYEL  "\x1B[33m"
#define KBLU  "\x1B[34m"
#define KMAG  "\x1B[35m"
#define KCYN  "\x1B[36m"
#define KWHT  "\x1B[37m"

int main()
{
    printf("%sred\n", KRED);
    printf("%sgreen\n", KGRN);
    printf("%syellow\n", KYEL);
    printf("%sblue\n", KBLU);
    printf("%smagenta\n", KMAG);
    printf("%scyan\n", KCYN);
    printf("%swhite\n", KWHT);
    printf("%snormal\n", KNRM);

    return 0;
}
WedaPashi
  • 3,561
  • 26
  • 42
Kritpal Singh
  • 475
  • 5
  • 10
26

This is a little function that prints colored text using bash scripting. You may add as many styles as you want, and even print tabs and new lines:

#!/bin/bash

# prints colored text
print_style () {

    if [ "$2" == "info" ] ; then
        COLOR="96m";
    elif [ "$2" == "success" ] ; then
        COLOR="92m";
    elif [ "$2" == "warning" ] ; then
        COLOR="93m";
    elif [ "$2" == "danger" ] ; then
        COLOR="91m";
    else #default color
        COLOR="0m";
    fi

    STARTCOLOR="\e[$COLOR";
    ENDCOLOR="\e[0m";

    printf "$STARTCOLOR%b$ENDCOLOR" "$1";
}

print_style "This is a green text " "success";
print_style "This is a yellow text " "warning";
print_style "This is a light blue with a \t tab " "info";
print_style "This is a red text with a \n new line " "danger";
print_style "This has no color";
Arian Acosta
  • 6,491
  • 1
  • 35
  • 32
  • 2
    what is your policy on re-use of code posted to StackOverflow? – Daisuke Aramaki Jun 21 '19 at 12:33
  • 2
    @DaisukeAramaki Thanks for asking. You may use in any way and anywhere you want. It's under the MIT license. If you want to collaborate, feel free to submit improvements to the Gist: https://gist.github.com/arianacosta/d6d1c521d231cc09ec5fe850ae2f5be1 – Arian Acosta Jun 21 '19 at 18:13
4

I use this c code for printing coloured shell output. The code is based on this post.

//General Formatting
#define GEN_FORMAT_RESET                "0"
#define GEN_FORMAT_BRIGHT               "1"
#define GEN_FORMAT_DIM                  "2"
#define GEN_FORMAT_UNDERSCORE           "3"
#define GEN_FORMAT_BLINK                "4"
#define GEN_FORMAT_REVERSE              "5"
#define GEN_FORMAT_HIDDEN               "6"

//Foreground Colors
#define FOREGROUND_COL_BLACK            "30"
#define FOREGROUND_COL_RED              "31"
#define FOREGROUND_COL_GREEN            "32"
#define FOREGROUND_COL_YELLOW           "33"
#define FOREGROUND_COL_BLUE             "34"
#define FOREGROUND_COL_MAGENTA          "35"
#define FOREGROUND_COL_CYAN             "36"
#define FOREGROUND_COL_WHITE            "37"

//Background Colors
#define BACKGROUND_COL_BLACK            "40"
#define BACKGROUND_COL_RED              "41"
#define BACKGROUND_COL_GREEN            "42"
#define BACKGROUND_COL_YELLOW           "43"
#define BACKGROUND_COL_BLUE             "44"
#define BACKGROUND_COL_MAGENTA          "45"
#define BACKGROUND_COL_CYAN             "46"
#define BACKGROUND_COL_WHITE            "47"

#define SHELL_COLOR_ESCAPE_SEQ(X) "\x1b["X"m"
#define SHELL_FORMAT_RESET  ANSI_COLOR_ESCAPE_SEQ(GEN_FORMAT_RESET)

int main(int argc, char* argv[])
{
    //The long way
    fputs(SHELL_COLOR_ESCAPE_SEQ(GEN_FORMAT_DIM";"FOREGROUND_COL_YELLOW), stdout);
    fputs("Text in gold\n", stdout);
    fputs(SHELL_FORMAT_RESET, stdout);
    fputs("Text in default color\n", stdout);

    //The short way
    fputs(SHELL_COLOR_ESCAPE_SEQ(GEN_FORMAT_DIM";"FOREGROUND_COL_YELLOW)"Text in gold\n"SHELL_FORMAT_RESET"Text in default color\n", stdout);

    return 0;
}
Jonny Schubert
  • 1,393
  • 2
  • 18
  • 39
  • Why not use the existing `tput` program that selects appropriate codes for the actual terminal? This takes no account of the terminal type. – Toby Speight Oct 10 '21 at 09:12
  • @Toby Speight I agree, using tput is the best solution when available. This is more about the basic principle. I used this on a embedded platform. – Jonny Schubert Oct 20 '21 at 08:45
4

man printf.1 has a note at the bottom: "...your shell may have its own version of printf...". This question is tagged for bash, but if at all possible, I try to write scripts portable to any shell. dash is usually a good minimum baseline for portability - so the answer here works in bash, dash, & zsh. If a script works in those 3, it's most likely portable to just about anywhere.

The latest implementation of printf in dash[1] doesn't colorize output given a %s format specifier with an ANSI escape character \e -- but, a format specifier %b combined with octal \033 (equivalent to an ASCII ESC) will get the job done. Please comment for any outliers, but AFAIK, all shells have implemented printf to use the ASCII octal subset at a bare minimum.

To the title of the question "Using colors with printf", the most portable way to set formatting is to combine the %b format specifier for printf (as referenced in an earlier answer from @Vlad) with an octal escape \033.


portable-color.sh

#/bin/sh
P="\033["
BLUE=34
printf "-> This is %s %-6s %s text \n" $P"1;"$BLUE"m" "blue" $P"0m"
printf "-> This is %b %-6s %b text \n" $P"1;"$BLUE"m" "blue" $P"0m"

Outputs:

$ ./portable-color.sh
-> This is \033[1;34m blue   \033[0m text
-> This is  blue    text

...and 'blue' is blue in the second line.

The %-6s format specifier from the OP is in the middle of the format string between the opening & closing control character sequences.


[1] Ref: man dash Section "Builtins" :: "printf" :: "Format"

AaronDanielson
  • 2,230
  • 28
  • 29
  • No, the most portable way is to generate them with `tput`, rather than assuming one set of terminal-specific escapes will work everywhere (clue: it won't). – Toby Speight Oct 10 '21 at 09:13
  • @TobySpeight, hi! I see from your comments on other answers you're a `tput` evangelist. To your issue with this answer, ASCII escape characters are definitely not "terminal-specific escapes", and this answer doesn't rely on an extra utility which may or may not be available. – AaronDanielson Oct 20 '21 at 14:15
  • No, these aren't just ASCII. They are completely terminal-dependent. Use these on any terminal that doesn't support the specific codes you use and you'll at best get garbage mixed in with output (and at worst, unexpected behaviour). That's something I find irritating. Ignoring `$TERM` is just rude to your users. – Toby Speight Oct 20 '21 at 19:49
1
color() {
    STARTCOLOR="\e[$2";
    ENDCOLOR="\e[0m";
    export "$1"="$STARTCOLOR%b$ENDCOLOR" 
}
color info 96m
color success 92m 
color warning 93m 
color danger 91m 

printf $success'\n' "This will be green"; 

This declares all your colors. If the colors might not be available, you might want a way to fallback. You could write:

printf ${danger:-'%b'}'\n' "This will be red and would not break if color is unavailable"; 

Or if you want to compose a message over multiple lines, just leave out the '\n'

printf $info "This is "; 
printf $info "blue!"; 
printf '\n'
Nico
  • 872
  • 10
  • 16
-6
#include <stdio.h>

//fonts color
#define FBLACK      "\033[30;"
#define FRED        "\033[31;"
#define FGREEN      "\033[32;"
#define FYELLOW     "\033[33;"
#define FBLUE       "\033[34;"
#define FPURPLE     "\033[35;"
#define D_FGREEN    "\033[6;"
#define FWHITE      "\033[7;"
#define FCYAN       "\x1b[36m"

//background color
#define BBLACK      "40m"
#define BRED        "41m"
#define BGREEN      "42m"
#define BYELLOW     "43m"
#define BBLUE       "44m"
#define BPURPLE     "45m"
#define D_BGREEN    "46m"
#define BWHITE      "47m"

//end color
#define NONE        "\033[0m"

int main(int argc, char *argv[])
{
    printf(D_FGREEN BBLUE"Change color!\n"NONE);

    return 0;
}
Draken
  • 3,134
  • 13
  • 34
  • 54
Thlv
  • 23
  • 2