Bash – Is it possible to change font colors in the terminal, without impacting printf’s “%*s” padding format

bashcolorsterminaltput

I have a function in a bash script: message_offset which is used to print the status of a bash script.
i.e. you would call it passing a message into it and a status, like this

message_offset "install font library" "[   OK   ]" 

and it would print into the terminal where printf's %*s format is used to always set the rightmost character of [ OK ] at 80 columns wide
e.g. output would be

install font library                              [   OK   ]
update configuration file on server               [   ERR  ]
                                                           ^
                                                           |
                                                      always
                                                        at 80

If echo was used output would look like this

install font library                 [   OK   ]
update configuration file on server               [   ERR  ]

code:

#!/usr/bin/env bash

function message_offset() {

    local message="$1"
    local status="$2"

    # compensate for the message length by reducing the offset 
    # by the length of the message, 
    (( offset = 80 - ${#message} ))

    # add a $(tput sgr0) to the end to "exit attributes" whether a color was
    # set or not
    printf "%s%*s%s" "${message}" 80 "$status" "$(tput sgr0)"

}

this all works ok, until I try to use tput to add some color sequences into the string, i.e. to make "[ ERR ]" red.
It seems that the printf "%*s" formatting is counting
the tput character sequences when its setting the offset, so if I call the function like this

message_offset "update configuration file on server"  "$(tput setaf 1)[   ERR  ]"

the output will look something like:

install font library                              [   OK   ]
update configuration file on server          [   ERR  ]

because printf "%*s" is saying hey this string has got all the "[ ERR ]" characters, plus the "$(tput setaf 1) chars, but obviously the "$(tput setaf 1) chars are not printed, so don't actually affect the padding.
Is there a way I can add color the "status" messages, and also use the tput style color sequences?

Best Answer

You're making this a lot more complicated than it should be. You can handle alignment with $message and not care about the width of ANSI sequences:

#! /usr/bin/env bash

message() {
    [ x"$2" = xOK ] && color=2 || color=1
    let offset=$(tput cols)-4-${#2}
    printf "%-*s[ %s%s%s ]\n" $offset "$1" "$(tput setaf "$color")"  "$2" "$(tput sgr0)"
}  

message "install font library" "OK"
message "update configuration file on server" "ERR"

Edit: Please note that most printf(1) implementations don't cope well with lengths calculations for multibyte charsets. So if you want to print messages with accented characters in UTF-8 you might need a different approach. shrug

Related Question