7

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?

8

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

  • thanks that works and is definitely cleaner, now Im trying to understand how your code is working. for example, can you tell me what the difference between the printf %*s and the printf %-*s formats are? – the_velour_fog Mar 19 '17 at 9:16
  • @the_velour_fog $(tput cols) = terminal width, printf '%-*s' $offset "$1" prints the message left-aligned, and pads it to the right with blanks up to width $offset. That's essentially all there is to it. – Satō Katsura Mar 19 '17 at 9:19
  • hmm, still not getting it, but it seems, printf '%-*s' is the key here? – the_velour_fog Mar 19 '17 at 9:21
  • Try this: printf '%10s|\n' aaa, then printf '%-10s|\n' aaa, then printf '%-*s|\n' 10 aaa – Satō Katsura Mar 19 '17 at 9:27
  • ah, thanks, got it. yes %-*s is essentially "absolute" padding regardless of how long %s is, whereas %*s is "relative to the right edge of the previous word. thanks Sato :) – the_velour_fog Mar 19 '17 at 9:33
1

an easy approach is to colorize everything after it has been aligned

In a nutshell you need

  • a function (or better, external script) to colorize string with colors (for example using perl's s,$regex,$color$&$resetcolor,gi

  • and you call it after you did the printing. color escape codes won't change the alignement that way.

for example: let's say you created a script named "colorize" that takes colors arguments, followed by regexes to be colorized with that color: for exemple colorize -blue 'regex1' -green 'regex2' you call it when needed:

 { code
   that
  formats and display things
 } | colorize -red 'ERR' -green 'OK'

Having that as a script by itself allow you to use it everywhere, for exemple:

 df -h | colorize -red '[890].%'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.