Formatting output with printf: truncating or padding
Asked Answered
G

3

7

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?

Grayback answered 21/3, 2016 at 0:2 Comment(3)
Where does the data come from?Evannia
And are you sure that's what you tried? I'd think your format string would produce - between the fields.Evannia
The fields are read in from a file of unknown length. The actual command issued is printf '%-14s -%3s -%3s\n' $name $weight $height.Grayback
E
10

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.

Evannia answered 21/3, 2016 at 0:48 Comment(0)
U
7

a more idiomatic awk version will be

$ awk        '{v=$1 FS $2} 
  length(v)>14{v=substr(v,1,11)"..."}
              {printf "%-15s %-3d %-3d\n",v,$3,$4}' file     

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

if you remove the - sign in %-3d, numbers will be right aligned as usual, however this the format you asked for.

Unhelm answered 21/3, 2016 at 1:0 Comment(0)
C
5

This is a partial answer -- Ignoring the final "...":

awk '{printf "%-14.14s %-3d %-3d\n",$1 FS $2,$3,$4}'

where %-14.14s does the padding + truncate formating of the name

Casual answered 18/10, 2017 at 14:32 Comment(1)
This is what I needed to truncate with the optional printf argument to __git_ps1. Note that you can truncate to 14 without padding with %.14s and I opted to skip the '...' since my goal is to limit my PS1 length.Bergius

© 2022 - 2024 — McMap. All rights reserved.