1

I am trying to implement a shell function to print a specific number of "space" characters, to provide padding for fixed-width console output.

#!/bin/bash

function printWhiteSpaces
{
  local i="122"
  local len="$1"
  local whitespace=' '
  local k=`expr $i - $len`
  while [ $k -gt 0 ]
  do
    echo "${whitespace}"
    ((k--))
  done
}

The above function takes in a single argument as a "length of something", then I take the difference of the desired field width (122) and the argument $1. And I wish to print that many whitespace characters.

Why won't the above print any whitespaces? If anyone has a solution or an improvement to the above function please share. In particular I want to know if there's a better way to write this function that prints concatenated whitespace characters.

Solution so far:

#!/bin/bash

function printWhiteSpaces
{
  local i="122"
  local len="$1"
  local k=`expr $i - $len`
  local whitespace=' '
  while [ $k -gt 0 ]
  do
    echo -n "${whitespace}"
    ((k--))
  done
}
0
2

If your aim is to simply print a string variable with a fixed, whitespace-padded field length, I would recommend using the printf function of bash:

printf "%122s" "$myvar"

will print the content of $myvar with as much space to the left as is necessary to fill 122 characters in total.

Try this simple test (field width taken as 20 for readability):

~> myVar="Hi, there!"; printf '%20s\n' "$myVar"
          Hi, there!
~> myVar="Bye!"; printf '%20s\n' "$myVar"
                Bye!

Your original approach didn't work because echo will implicitly print a newline at the end.

If you want to use a shell loop and only echo, you have to call it as echo -n to inhibit the newline:

~> myVar="Bye!"; l=${#myVar}; for ((i=20;i>l;i--)); do echo -n ' '; done; echo "$myVar"
                Bye!
6
  • @ChristopherTruong Note that printfrequires a format string as its first argument. – Kusalananda Mar 17 at 8:50
  • While the printf seems like a better solution, if you're determined to use echo, you could make 'whitespace' a string of 122 spaces and do something like echo "${whitespace:0:`expr $i - $len`}" – Brandon Xavier Mar 17 at 8:55
  • @BrandonXavier Your suggestion gave me only a single character. – Christopher Truong Mar 17 at 9:02
  • My mistake - replace the `expr $i - $len` with just $k (the expr won't see the unexported variables) – Brandon Xavier Mar 17 at 9:04
  • Like this: echo -n "${whitespace:0:$k}" ? I still get a single character. – Christopher Truong Mar 17 at 9:11
1

This script (tested with bash and sh):

#!/bin/sh

function printWhiteSpaces() {
  local i="122"
  local len="$1"
  local k=`expr $i - $len`
  local whitespace='                                                                                                                                                                  '
  echo "${whitespace:0:$k}" test
}

printWhiteSpaces 100
printWhiteSpaces 110
printWhiteSpaces 120

Produced the following output, which I believe is what you wanted:

                       test
             test
   test

(This was done on a Mac - doubt that would make a difference though)

3
  • sh on a mac might be some feature-rich shell. function foo() is a ksh-ism, the POSIX declaration is just foo() { ... }. You could use $(( i - len )) instead of expr and And ${whitespace:0:$k} sadly isn't posix, so won't work on a feature-restricted sh like Dash. – ilkkachu Mar 17 at 11:44
  • Just tested on a CentOS 7.9 system - the script behaved correctly under both bash and sh, and with/without the function in the declaration (which, BTW, was copied from the OP script) – Brandon Xavier Mar 17 at 13:21
  • 1
    sh on CentOS might also be Bash. I don't doubt it works on Bash, I'm just pointing out that foo() works more widely, without a downside. Debian/Ubuntu has Dash as /bin/sh by default, and it's not once or twice that people have tripped on that when implicitly assuming all shells are Bash. See e.g. wiki.ubuntu.com/DashAsBinSh and lwn.net/Articles/343924 . Busybox's sh is similar to Dash, and comes up often-ish on embedded systems. – ilkkachu Mar 17 at 13:35
0

Inside the function we can tamper with the command line . So we freshly create a command line of $k number of arguments (all null). After the construction is done we echo the command line $* in double quotes will give IFS first char(= space ) separated arguments and since the arguments were all null, we will see only a sea of spaces.

function printWhitespaces
{
  local i="122"
  local len="$1"
  local whitespace=' '
  local k=`expr $i - $len`
  set -- ""
  while [ "$#" -le "$k" ]
  do
    set -- "$@" "$1"
  done
  echo "$*"
}

And another approach, if you already have computed the $k variable is:

printf '%*s\n' "$k" ""

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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