bashawkprintfoutputformatted

Formatting output with printf: truncating or padding


I would like to produce the following output:

> Avril Stewart  99  54
> Sally Kinghorn 170 60
> John Young     195 120
> Yutte Schim... 250 40

As you can see, names shorter than 14 characters are padded with spaces. Names longer than 15 characters are truncated: 'Yutte Schimmelpenninck' truncates to 'Yutte Schim...'.

Here is what I have tried to achieve this (the variables $name, $height, and $weight are extracted from files, and a loop runs the printf command on each file data):

printf '%-14s -%3s -%3s\n' "$name" "$height" "$weight"
> Avril Stewart  99  54
> Sally Kinghorn 170 60
> John Young     195 120
> Yutte Schimmelpenninck 250 40

A printf one-liner is the desired solution.

What code will produce the first block of output?


Solution

  • Before your printf command, you want to check if a name is longer than 14 characters, and if yes, truncate it and replace the last three characters with dots. This command does that:

    (( ${#name} > 14 )) && name="${name:0:11}..."
    

    It replaces name with its first eleven characters and appends ....

    You also have to fix the printf format string: instead of

    '%-14s -%3s -%3s\n'
    

    it has to be

    '%-14s %-3s -%-3s\n'
    

    or you get results such as

    Avril Stewart  - 99 - 54
    

    Maybe that was just a typo, though, as your example output didn't have the hyphens.

    All in all:

    $ name='Avril Stewart'; weight=99; height=54
    $ (( ${#name} > 14 )) && name="${name:0:11}..."
    $ printf '%-14s %-3s %-3s\n' "$name" $weight $height
    Avril Stewart  99  54
    $ name='Sally Kinghorn'; weight=170; height=60
    $ (( ${#name} > 14 )) && name="${name:0:11}..."
    $ printf '%-14s %-3s %-3s\n' "$name" $weight $height
    Sally Kinghorn 170 60
    $ name='Yutte Schimmelpeninck'; weight=250; height=40
    $ (( ${#name} > 14 )) && name="${name:0:11}..."
    $ printf '%-14s %-3s %-3s\n' "$name" $weight $height
    Yutte Schim... 250 40
    

    So if you read this from a file, for example comma separated, you'd end up with a loop like this:

    while IFS=, read -r name weight height; do
        (( ${#name} > 14 )) && name="${name:0:11}..."
        printf '%-14s %-3s %-3s\n' "$name" $weight $height
    done < inputFile
    

    resulting in

    Avril Stewart  99  54
    Sally Kinghorn 170 60
    John Young     195 120
    Yutte Schim... 250 40
    

    I don't think it's possible in a one-liner. I experimented with the ternary operator and tried something like

    printf '%s\n' $(( ${#name} > 14 ? "${name:0:11}..." : "$name" ))
    

    but the problem here is that it only works for integers, and strings expand to zero in arithmetic context.