3

Using tput changes the length of the string and so columns are not aligned. How to fix this?

Tried following code in bash script.

B="$(tput bold)" # Bold text
N="$(tput sgr0)" # Normal text

function testing(){

IN="KEYS | VALUES | DESCRIPTION
id | ${B}10${N} or ${B}20${N} | Enter ID. Default is ${B}10${N}.
status | ${B}true${N} or ${B}false${N} | Enter status of something. Default is ${B}true${N}.
style | Example: Standard | Give suitable standard."

    IFS= 
    while read -r lines; do
        IFS='|' read -r -a array <<< "$lines"
        printf "%-35s %-35s %-s\n" "${array[0]}" "${array[1]}" "${array[2]}"
    done <<< "$IN"

    read -p "$*"
    exit 0
}

Output is something like:

    KEYS          VALUES                              DESCRIPTION
    id            **10** or **20**           Enter ID. Default is **10**.
    status        **true** or **false**      Enter status of something. Default is **true**.
    style         Example: Standard                   Give suitable standard.

Expected to be:

    KEYS          VALUES                              DESCRIPTION
    id            **10** or **20**                    Enter ID. Default is **10**.
    status        **true** or **false**               Enter status of something. Default is **true**.
    style         Example: Standard                   Give suitable standard.
2
  • 2
    when I run the code in my bash env I find that the 2 problematic DESCRIPTION strings lose 20 spaces of indent; lengths of the vars (${#B} & ${#N}) are 4 and 6 chars, respectively; these vars show up twice in the VALUES field for a total of 20 (non-printable) chars; printf/%s counts the 20 non-printable chars as part of the output hence the appearance that 20 spaces are 'lost' (ie, printf is printing 35 chars ... it's just that 20 of those chars are non-printing); "Duh, Mark!" ? what I don't know is: does printf have a flag to ignore non-printable chars for format purposes?
    – markp-fuso
    Commented Aug 29, 2019 at 17:56
  • Possible duplicate of Color escape codes in pretty printed columns
    – KamilCuk
    Commented Aug 29, 2019 at 19:44

1 Answer 1

3

As I mentioned in my comment:

  • when I run the code in my bash env I find that the 2 problematic DESCRIPTION strings lose 20 spaces of indent
  • lengths of the vars (${#B} & ${#N}) are 4 and 6 chars, respectively
  • these vars show up twice in the VALUES field for a total of 20 (non-printable) chars
  • printf/%s counts the 20 non-printable chars as part of the output hence the appearance that 20 spaces are 'lost' (ie, printf is printing 35 chars ... it's just that 20 of those chars are non-printing)

What if we measure the length of the second field - with and without the special chars - and then use the difference (ie, the number of non-printable characters) to increase the format width of the second field of the printf command?

We'll store the length of the second field (which includes our special characters) in a new variable len2:

len2="${#array[1]}"

Next we want to strip out the special characters and measure the length of the resulting string and place in variable lenx:

x="${array[1]//${B}/}"    # strip out all occurrences of variable 'B'
x="${x//${N}/}"           # strip out all occurrences of variable 'N'
lenx=${#x}

NOTE: I had some problems getting tr and sed to properly strip out the special characters; I'm open to suggestions. On the plus side ... I'm not spawning any sub processes.

We'll store the new format width in variable w2 ('w'idth for field '2') like such:

w2=$(( 35 + len2 - lenx ))

And the new printf format string becomes:

printf "%-35s %-${w2}s %-s\n" ...

Pulling it all together we get:

B="$(tput bold)" # Bold text
N="$(tput sgr0)" # Normal text

function testing(){

IN="KEYS | VALUES | DESCRIPTION
id | ${B}10${N} or ${B}20${N} | Enter ID. Default is ${B}10${N}.
status | ${B}true${N} or ${B}false${N} | Enter status of something. Default is ${B}true${N}.
style | Example: Standard | Give suitable standard."

    IFS= 
    while read -r lines; do
        IFS='|' read -r -a array <<< "$lines"

        len2="${#array[1]}"

        x="${array[1]//${B}/}"
        x="${x//${N}/}"
        lenx=${#x}

        w2=$(( 35 + len2 - lenx ))
   #    echo "w2 = ${w2}"

        printf "%-35s %-${w2}s %-s\n" "${array[0]}" "${array[1]}" "${array[2]}"
    done <<< "$IN"

    read -p "$*"
    exit 0
}

Running the script in my bash env generates:

$ testing
KEYS                                 VALUES                              DESCRIPTION
id                                   10 or 20                            Enter ID. Default is 10.
status                               true or false                       Enter status of something. Default is true.
style                                Example: Standard                   Give suitable standard.

NOTE: The bold fields do print as bold on my terminal ... they just don't copy/display as bold in the above answer.

If you uncomment the line - echo "w2 = ${w2}" - you should find (for the 2 lines of interest) that we're going to use a format width of 55 for the second field (ie, the desired 35 plus an extra 20 to compensate for the 20 characters worth of non-printable characters).

2
  • Your solution works exactly as needed. Thanks for the help! Hopefully in future there will be a flag for printf for such cases.
    – R. Adams
    Commented Aug 30, 2019 at 10:59
  • Good to hear, though doubtful we'll see such a flag anytime (soon)
    – markp-fuso
    Commented Aug 30, 2019 at 13:11

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