1. Overview

In this tutorial, we’ll explore various methods for reversing the order of words in a Bash string. This task can be useful in many situations, such as when displaying filenames in reverse chronological order or reversing the order of arguments passed to a script.

In particular, we’ll examine using the tac command along with xargs and tr, using the awk command, writing a custom script, and using arrays. Except for the POSIX-standard awk, the commands we explore in this tutorial are part of the coreutils package. All these commands come preinstalled on most Linux distributions.

2. Using tac

One way to reverse the order of words in a Bash string is the tac command. tac is a tool that is typically used to reverse the order of lines in a file or input. The name tac is a play on the word cat, with the letters reversed. This is because tac functions similarly to cat in that it combines and displays the content of specified files but in reverse order.

To reverse the order of words in a string with tac, we should first break the string into individual words on separate lines. We can do this by converting spaces into newlines using the tr command. We then reverse the order of lines using tac. Finally, we arrange the words on a single line using xargs:

$ str='one two three four five'
$ echo "$str" | tr ' ' '\n' | tac | xargs
five four three two one

The default command for xargs is echo when no command is specified after it.

Alternatively, for the initial step, we can also use xargs in place of tr to separate the words in the string by newline characters:

$ str='one two three four five'
$ echo "$str" | xargs -n 1 | tac | xargs
five four three two one

Adding the -n 1 option to xargs allows separating the words, one per line.

A more succinct solution would be to use the -s option with tac. The default separator used in tac is the newline character \n. However, by using -s, we can specify a space separator, enabling us to directly use tac on the here-string:

$ str='one two three four five'
$ tac -s ' ' <<< "$str" | xargs
five four three two one

Additionally, it’s possible to eliminate the xargs command at the end by echoing the result given as the output of a subshell:

$ str='one two three four five'
$ echo $(tac -s ' ' <<< $str)
five four three two one

The subshell output will show five on a separate line from the other words due to the included newline character, meaning it appears as “five\n“. However, the use of echo resolves the problem. This is because echo $string doesn’t preserve newlines, as opposed to echo “$string” which does.

3. Using awk

Another method to reverse the words in a Bash string is the awk command. awk is a powerful text processing tool that can be used to manipulate strings in a variety of ways.

Let’s reverse the order of words in a string using awk:

$ str='one two three four five'
$ echo "$str" | awk '{ for (i=NF; i>0; i--) printf("%s ",$i); printf("\n")}'
five four three two one

The awk command first breaks the string into words using the default IFS (Internal Field Separator), which is a space. Then it iterates through the words in reverse order using the predefined awk variable NF (Number of Fields), here representing the total number of words in the string. The first printf function prints each word followed by a space. The second printf function prints a newline character to maintain the one found at the end of the initial string.

Alternatively, we can loop backwards up until the first word, and then print that word separately. This way we preserve the newline character at the end:

$ str='one two three four five'
$ echo "$str" | awk '{ for (i=NF; i>1; i--) printf("%s ",$i); print $1 }'
five four three two one

Yet another way is to append either a space or a newline after each word, depending on a ternary expression:

$ str='one two three four five'
$ echo "$str" | awk '{ for(i=NF; i>0; i--) printf("%s%s",$i,(i>1?OFS:ORS)) }'
five four three two one

The awk command will print each word followed by either the OFS (Output Field Separator), which defaults to a space, or the ORS (Output Record Separator), which defaults to a newline. The command uses a space as a separator unless the current word is the final word in the iteration, in which case it uses a newline.

4. Using a Custom Script

We can also write a custom script to reverse the order of words in a string:

$ cat script.sh
str='one two three four five'
reverse() {
    result=''
    for i in $@; do
        result="$i $result"
    done
    echo "$result" 
}
reverse "$str"

In the function reverse, we iterate through each word and prepend the current word to the variable result. This way we reverse the order of the words, and we finally echo the result.

Running the script, we get the required result:

$ bash script.sh
five four three two one

Alternatively, we may use seq to loop over the words backwards as we echo the words one by one:

$ cat script.sh
str='one two three four five'
reverse() {
    for i in `seq ${#@} -1 1`; do
        echo ${!i}
    done
}
echo $(reverse $str)

The expression ${!i} is known as indirect expansion or parameter indirection and basically means that the value of variable i gets used as the name of the variable to be expanded. So for example, when i=1, the expression ${!i} equates to ${1}, which is the first argument of the reverse function.

It’s important to note that the above function returns one word per line. That’s why we run it in a subshell and use echo to output the final string on a single line.

5. Using Arrays

The final method we’ll discuss consists of several steps:

  1.  Convert a string into an array of words
  2.  Use a for loop to iterate through the array in reverse order and print each element
  3.  Concatenate the words to form the reversed string

Here’s an example:

$ cat script.sh
str='one two three four five'
array=($str)
for i in `seq $((${#array[@]} - 1)) -1 0`; do
    echo "${array[$i]}"
done | xargs

In this approach, we use seq to iterate backwards over the elements of the array and echo each one. Lastly, we pipe the output into xargs to display the result over a single line.

6. Conclusion

In this article, we discussed several methods for reversing the order of words in a Bash string, including using the tac command, the awk command, creating a custom script, and using arrays.

The choice of method depends on the specific requirements of the task at hand. For simple and short strings, the tac command could be a good choice, while more complex strings may require the awk command or a custom script.