I wrote the following function in ksh that prints its first argument to the screen and underlines it with the appropriate ammount of - character:

print_underlined () {
    echo $word

    while [[ i -gt 0 ]]; do
        printf "-"
        (( i = $i - 1 ))
    printf "\n"


$ print_underlined foobar

I wonder if there is a simpler and more elegant way to display an underlined word to the screen.

For the record I am using:

  • Solaris 10
  • ksh88

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' '_')

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 -)
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 () {
        printf %s\\n "$word"
        while (( i )); do
                printf -
                (( i = i - 1 ))

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.

  • Unfortunately, except for the sed process, none of the solutions suggested seem to work on such an old shell (although I tested them in bash and they seem to work). The decrement operator in particular is annoyingly missing from the shell, hence my original clumsy syntax. Oh and the tput solution didn't work either... it was too good to be true :( – rahmu May 14 '12 at 13:35
  • @rahmu Sorry, I got carried away and didn't realize ksh88 lacked the decrement operator :). A pure POSIX-sh approach would not be much different from what you wrote, just with different test and arithmetic operators. The tput approach is entirely dependent on the terminal. If you want your script to be portable, it's probably not a good idea to assume things about the user's terminal. – jw013 May 14 '12 at 13:42

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.

  • Also a second optional argument can be provided for the underline character. – nikitautiu May 14 '12 at 13:43
  • Nope won't work on my old shell. The answer is similar to the first one suggested by jw013 and is analogous to what I am looking for. – rahmu May 14 '12 at 13:52

This is a POSIX compliant way that will work on Solaris 10 & ksh88:

print_underlined () {
  printf "%s\n%s\n" "$1" $(printf "%s\n" "$1" | sed "s/./-/g")

$ print_underlined "hello world"
hello world
print_underlined () {

tput smul
print $word
tput sgr0

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.