1

My script-in-progress displays a multi-column colorized table, but the color-codes are interfering with the formatting. The color codes cannot be moved to the format string because the coloring of some columns is variable from row to row. No solutions are given in related Q&A's (cited below). I've provided pared down examples and work-arounds below.

My actual usage is a bash script displaying an 11 column table with different columns in different colors, generated while looping through a bunch of jpg files, analyzing various exif data and outputing results as a row in the table, with some colors also varying from row to row based on the exif analysis results.

But as I said, I've provided pared down examples work-arounds below.

Here is a simplified display-table snippet that demonstrates the issue:

# RedBlk defined in .bashrc.local as RedBlk="^[[0;31;40m"
# DefDef defined in .bashrc.local as DefDef="^[[0m"
(GrnBlk=$(tput setaf 2)
 YelBlk="\e[1;33m"
 echo "123456789 123456789"
 for ii in {8..13}; do
 case $ii in
   8|9) clr1=""; clr2="";;
    11) clr1=$RedBlk; clr2="";;
    12) clr1=$YelBlk; clr2=$GrnBlk;;
     *) clr1=$GrnBlk; clr2=$RedBlk;;
  esac
  printf "%6d %6b$DefDef %6b\n"   $((ii*2))   $clr1$ii    $clr2$((ii*4)) 
done)

.... with output:

123456789 123456789
    16      8     32
    18      9     36
    20 10 40
    22 11     44
    24 12 48
    26 13 52

Note the same problem occurs using "^[[0;31;40m" or $(tput setaf 2) or "\e[1;33m"

Here is an oversimplified example:

(echo "123456789 123456789 123456789"
 printf "%20s\n" "Hello""Again"
 printf "%20s\n" $RedBlk"Hello"$DefDef"Again"
)

.... with output:

123456789 123456789 123456789
          HelloAgain
HelloAgain

Two work-arounds are 1) alter the field width (e.g. from 20 to 20+${#RedBlk}+${#DefDef} = 20+10+4 in the over simplified example), and 2) split up the string and hack the format:

(echo "123456789 123456789 123456789"
 printf "%20s\n"       "Hello""Again"
 printf "%34s\n"       $RedBlk"Hello"$DefDef"Again"
 printf "%s%15s%s%s\n" $RedBlk "Hello" $DefDef "Again"
)

.... with output:

123456789 123456789 123456789
      HelloAgain
      HelloAgain
      HelloAgain

But both work-arounds are ultra clumsy given actual usage.

These Q&A's, though related, do not provide a solution:

https://stackoverflow.com/questions/67638971/how-to-get-color-and-width-formatting-with-printf

https://stackoverflow.com/questions/58519511/bash-printf-formated-output-with-colors

https://stackoverflow.com/questions/5412761/using-colors-with-printf

How to use printf and %s when there are color codes?

What are some solutions that are more simple?

This is what I am using:

bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

# which printf
/usr/bin/printf
# /usr/bin/printf --version
printf (GNU coreutils) 8.26

# /usr/bin/xterm -version
XTerm(327)
3
  • You're running into the "invisible character" problem. The colorizing character strings take up space in the buffer, but don't appear on screen. bash handles this in PROMPTING with \[ and \]. Keep careful track of both the position in the buffer and the position on the screen
    – waltinator
    Sep 22, 2021 at 21:27
  • @waltinator I read PROMPTING on the bash manpage, but I can't figure out how to make use of \[ and \] with printf, for example printf "%10s\n" "\["$RedBlk'\]'"aaaa"\[$DefDef\] yields \[\]aaaa[]
    – Mic
    Sep 23, 2021 at 1:19
  • I said they were bash PROMPTING uses.
    – waltinator
    Sep 23, 2021 at 1:25

2 Answers 2

0

One solution is to dynamically create the format string on each iteration of the loop, as in

 fmt=`printf  "%b"  "%6d "$clr1"%6d$DefDef "$clr2"%6d"$DefDef`
 printf "$fmt\n"   $((ii*2))   $ii    $((ii*4)) 

full:

(DefDef=$(tput init)
 RedBlk=$(tput setaf 1)
 GrnBlk=$(tput setaf 2)
 YelBlk="\e[1;33m"
 echo "123456789 123456789"
 for ii in {8..13}; do
 case $ii in
   8|9) clr1=""; clr2="";;
    11) clr1=$RedBlk; clr2="";;
    12) clr1=$YelBlk; clr2=$GrnBlk;;
     *) clr1=$GrnBlk; clr2=$RedBlk;;
 esac
 fmt=`printf  "%b"  "%6d "$clr1"%6d$DefDef "$clr2"%6d"$DefDef`
 printf "$fmt\n"   $((ii*2))   $ii    $((ii*4)) 
done)

.... with output:

123456789 123456789
    16      8     32
    18      9     36
    20     10     40
    22     11     44
    24     12     48
    26     13     52
0

Another solution is to surround each field with identical length COLOR_ON(color) and COLOR_OFF sequences (color is usually black). Work with that length.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .