Formatting the output: Underlining

The core of your question is building a string consisting entirely of underscores that is of the same length as an existing string. In recent enough versions of bash, ksh or zsh you can build this string with the ${VARIABLE//PATTERN/REPLACEMENT} construct: underlines=${word//?/_}. But this construct doesn't exist in ksh88.

In any shell, you can use tr instead. POSIX-compliant implementations of tr let you write this:

underlines=$(printf %s "$word" | tr -c '_' '[_*]')

I think Solaris 10 has a POSIX-compliant tr by default, but there might be a historical implementation (compatible with earlier Solaris releases). Historical implementations of tr might not understand the [x*] syntax, but they tend to accept the following syntax instead (which isn't guaranteed by POSIX), to mean “replace everything that isn't a newline by a _”:

underlines=$(echo "$word" | tr -c '\010' '_')
underlines=${underlines%_}

And here's a slightly crazy method that doesn't use any loop or external program and should work in any Bourne shell (at least since set -f was introduced — though running in an empty directory would mitigate the lack of set -f). Unfortunately, it only works if the string doesn't contain any whitespace.

set -f          # turn off globbing
IFS=$word       # split at any character in $word
set a $word     # split $word into one word between each character, i.e. empty words
shift           # remove the leading a (needed in case $word starts with -)
IFS=_
underlines=$*   # join the empty words, separated with the new value of $IFS

A more complex variant deals with whitespace, but only if there isn't any consecutive whitespace sequence. I don't think you can go any further with this trick, since sequences of whitespace characters in IFS are always collapsed.

set -f
unset IFS; set a $0    # split at whitespace
IFS=$*; set $*         # split into empty words
IFS=_; underlines=$*   # collect the empty

In newer shells you can do printf %s\\n "${word//?/-}". I don't think ksh88 has that particular expansion.

If you don't mind an extra process, you could do printf %s\\n "${word}" | sed -e 's/./-/g'.

Your approach is fine as well, although I would make the following tiny change:

print_underlined () {
        word=$1
        printf %s\\n "$word"
        i=${#word}
        while (( i )); do
                printf -
                (( i = i - 1 ))
        done
        echo
}

And for a completely different approach, use the terminal's ability to display true underlines, if available:

tput smul; printf %s\\n "$word"; tput rmul

Of course, that approach only works if you know the terminal the script runs on supports it.


I found this simply by googling it:

underline() { echo $1; echo "${1//?/${2:--}}";}

Basically the same thing but much more compact. If you find it confusing, more info on the curly bracket substitution can be found here. Very similar to the sed syntax.

Tags:

Shell

Ksh

Solaris