39

My current code looks like this: x=${y:0:40}, which limits the length of string to 40 characters. In case of string being shorter than 40 characters, is it possible to fill the trailing places with spaces?

So if my y="very short text"

I would like my y to be:

y="very short text (+25 trailing spaces)"

4 Answers 4

43

You should try printf:

printf '%-40s' "$y"
1
  • 2
    I used this for numerical as follows: to obtain 0-left padded of length 5 printf '%5.5d' . For 230 it prints 00230 Dec 6, 2020 at 20:50
35

If those characters are all single-byte, that is if you're in a locale where the charset is single-byte (like iso8859-1) or if the locale's charset is UTF-8 but the text is ASCII only, you can do:

printf -v y %-40.40s "$y"

That will cover both truncating and padding.

If not, you can always add 40 spaces and use your ${y:0:40} approach.

printf -v pad %40s
y=$y$pad
y=${y:0:40}

zsh has dedicated operators for left and right padding:

y=${(r:40:)y}

(also does truncation). zsh's printf counts in characters instead of bytes (or based on the display width of the characters if also using the m parameter expansion flag), so wouldn't have bash's issue above. However note that you need zsh 5.3 or newer for the -v option.

See also this answer to a related question for more details if you're faced with characters that don't all have the same width.

1
  • the -v flag isn't supported on OSX, bash version 3.2.57
    – ekkis
    Mar 15, 2022 at 23:24
15

Pure bash:

ten="          " 
forty="$ten$ten$ten$ten" 
y="very short text"
y="${y:0:40}${forty:0:$((40 - ${#y}))}"
echo "'${y}'"

The method is to add 0-40 spaces to every string after truncating it.

Output, (note the single quote positions):

'very short text                         '
1
  • 1
    I would rather use this method, because it will works directly on variables and do not need to output. Aug 31, 2018 at 17:38
4

The usual recommendation to use printf is wrong, it counts bytes, not characters.
And the use of ${var:start:length}, even if it correctly (most of the time) counts characters, is limited to shells that have arrays.

A better way to count is based on ${#var} which should count characters.

$ for var in 1 123 ΐΐΐ ᾂᾂ "㉑㉒㉓" aáéí;
> do
>     printf '|%s%*s|\n' "$var" "$((10-${#var}))" "";
> done

|1         |
|123       |
|ΐΐΐ       |
|ᾂᾂ        |
|㉑㉒㉓       |
|aáéí      |

But the only portable way to trim the length of the string is to use grep.

#!/bin/sh
n=${1:-10}
for str in 1 123 123456789012 ΐ ΐΐΐ ΐΐΐΐΐΐΐΐΐΐ ΐΐΐΐΐΐΐΐΐΐΐΐΐΐΐΐΐΐΐΐ ᾂᾂᾂᾂᾂ "㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚" aáéíóúüñm
do
    str=$str$(printf '%*s' "$n" "")                     # pad with `n` spaces.
    var=$(echo "${str}"|grep -Eo "^.{1,$n}")            # limit length to `n`
    printf '|%s|\n' "${var}" 
done

Which prints:

|1         |
|123       |
|1234567890|
|ΐ         |
|ΐΐΐ       |
|ΐΐΐΐΐΐΐΐΐΐ|
|ΐΐΐΐΐΐΐΐΐΐ|
|ᾂᾂᾂᾂᾂ     |
|㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚|
|aáéíóúüñm |

Note that rounded numbers are double width. There are still 10 characters, but they ocupy twice the width. Note that trimming could be done in some shells with var=${str:0:n}.

printf

The implementation of printf is usually limited to count bytes (as per POSIX spec). Characters that are ASCII or some other one-byte-per-character have no problems. But most other international characters do:
$ printf '|%-10.9s|..\n' 1234567890 ΐ U+0390 ᾂ U+1F82 "㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚" aáéíóúüñm
|123456789 |..
|ΐ        |..
|U+0390    |..
|ᾂ       |..
|U+1F82    |..
|㉑㉒㉓ |..
|aáéíó |..

Note that printf should have printed 9 characters in an space of 10 characters, the %10.9s format. And it does with 1234567890, it cuts the numbers at 9 and prints it in 10 spaces |123456789 |... But with ΐ it fails on one position, on fails on two positions, on ㉑㉒㉓ fails on three positions, and finally on aáéíó fails on four positions.

There are two distinct problems here:

  • The first is the number of bytes each character is using. For example needs 3, that makes 3 of those characters 9 bytes (what was required of printf). In that case, 3 characters are counted as 9 bytes by printf.
  • The second is the width of each character. All of áéíóúüñ require two bytes and are of a width of one position (the same width of an space). Thus, printf prints aáéíó (one byte from a, two from the rest makes 9 bytes), only 5 characters, plus the trailing space, makes a total width of 6, short in 4 spaces.
  • In printing ㉑㉒㉓ each character needs 3 bytes but each character use a width of 2 spaces, making the string appear with a width of 6 spaces.

You must log in to answer this question.

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