How to reverse a list of words in a shell string?
Asked Answered
S

18

29

I have a list of words in a string:

str="SaaaaE SeeeeE SbbbbE SffffE SccccE"

I want to reverse it in order to get

"SccccE SffffE SbbbbE SeeeeE SaaaaE"

How I can do that with ash?

Starlight answered 30/12, 2014 at 10:31 Comment(0)
C
31

You can use awk as follows:

echo "$str" | awk '{ for (i=NF; i>1; i--) printf("%s ",$i); print $1; }'
Chellean answered 30/12, 2014 at 11:9 Comment(0)
Q
24

Yes, you can try these commands,

For string,

echo "aaaa eeee bbbb ffff cccc"|tr ' ' '\n'|tac|tr '\n' ' '

For the variable,

echo $str|tr ' ' '\n'|tac|tr '\n' ' '
Queasy answered 30/12, 2014 at 10:35 Comment(2)
tac is not supported in my ashStarlight
ok sorry I run this command in bash, but I'm keeping this command for future readers help.Queasy
M
12

if you need pure shell, no external tools, consider this:

reverse_word_order() {
    local result=
    for word in $@; do
        result="$word $result"
    done
    echo "$result" 
}

reverse_word_order "$str"

Otherwise tac can help you straight away:

echo -n "$str" | tac -s' '

or

tac -s' ' <<<"$str" | xargs 
Mussel answered 29/4, 2016 at 17:28 Comment(0)
E
9

For those wanting a simple one-line solution that isn't convoluted, tac is the way to go. Here is the simplest way I know using tac and command substitution:

echo $(tac -s ' ' <<< $str)
Embezzle answered 16/9, 2021 at 22:42 Comment(0)
P
8

You could use awk:

echo "aaaa eeee bbbb ffff cccc" | awk '{for(i=NF;i>0;--i)printf "%s%s",$i,(i>1?OFS:ORS)}'

Loop backwards through the fields, printing each one. OFS is the Output Field Separator (a space by default) and ORS is the Output Record Separator (a newline).

I'm assuming that you don't want the order of the letters in each word to be reversed.

Pitchman answered 30/12, 2014 at 10:36 Comment(0)
N
5

Here is an awk using do while (not much used here at Stackoverflow)
No extra variable needed i

echo "aaaa eeee bbbb ffff cccc"|awk '{do printf "%s"(NF>1?FS:RS),$NF;while(--NF)}'
cccc ffff bbbb eeee aaaa
Nell answered 30/12, 2014 at 11:11 Comment(3)
Slightly shorter awk '{do printf "%s"(NF>1?OFS:ORS),$NF;while (--NF)}'Mikkanen
@Jidder, thanks. Even shorter by using FS:RS instead of OFS:ORSNell
The effect of --NF is undefined behavior per POSIX so this will behave differently in different awk versions.Tear
D
3

If using Haskell is acceptable, there is a nice tool called hawk (haskell-awk) which can do this very easily. First you need to install it:

$ cabal install haskell-awk

Then:

$ echo $str | hawk -m reverse

You need ~/.cabal/bin in your PATH to find hawk.

Deboer answered 17/4, 2015 at 8:16 Comment(0)
R
3

This works in sh (I don't have ash so can't verify that):

reverse() {
    for (( i = ${#*}; i > 0; i-- ))
    {
        echo ${!i}
    }
}

use it like this:

$ reversed=$(reverse foo bar baz)
$ echo $reversed
baz bar foo

Here are some alternatives. First, one that doesn't use the C-style for:

reverse() {
    while [ ${#*} -gt 0 ]
    do
      echo ${*: -1}
      set -- "${@:1:$(($#-1))}"
    done
}

The next one (from here) works where substitutions don't (e.g. on Android)

reverse() {
    if [ "$#" -gt 0 ]; then
        local arg=$1
        shift
        reverse "$@"
        printf '%s\n' "$arg"
    fi
}

I was trying to find a solution that worked in an Android script, where sh doesn't like C-style for or complex substitution operations. I ended up with the last example because it uses neither.

Rachitis answered 4/9, 2016 at 9:23 Comment(1)
Which golfs to rwo(){ for((i=${#*};i>0;i--)){ echo ${!i};};}Vanegas
A
2

sed grouping:

str="SaaaaE SeeeeE SbbbbE SffffE SccccE"

echo $str | sed 's/\(.* \)\(.* \)\(.* \)\(.* \)\(.*\)/\5 \4\3\2\1/g'
Armoured answered 29/4, 2016 at 19:22 Comment(0)
V
1

The awk solution using do...while throws an error when the input string is empty, and some awk solutions using for loops will print a blank line when the input string is empty. This is slightly shorter than previous awk solutions, doesn't have the trouble of --NF having an undefined behavior in POSIX, and it doesn't throw an error or print a blank line when the input is an empty string:

$ echo "aaaa eeee bbbb ffff cccc" | awk '{i=NF;while(i)printf "%s",$i (i-->1?FS:RS)}'

cccc ffff bbbb eeee aaaa
Virtual answered 30/9, 2020 at 23:6 Comment(0)
T
0
/* Shell script to reverse the Input String */    
echo " **** Program to Reverse a String **** "
read -p " Enter Here : " text

echo "You have entered : " $text

echo -n "Reverse of String : "

arr=($text)

arrlength=${#arr[@]}

arrlength=`expr $arrlength - 1`

while [ $arrlength -ge 0 ]
do

echo -n ${arr[arrlength]}
echo -n " "
 arrlength=`expr $arrlength - 1`
done

echo

OUTPUT


**** Program to Reverse a String ***


Enter Here : I love My India

You have entered : I love My India

Reverse of String : India My love I

Tyree answered 11/4, 2015 at 20:54 Comment(1)
the awk answers are more instructive and useful. using the shell to read lines is contra-indicated. not to mention prompting the user. this is not what the command line is about.Abott
U
0

Another pure bash solution:

str='test a is this'; res=""; prev=""
while [ "$prev" != "$str" ]; do res="$res${str##* } "; prev="$str"; str="${str% *}"; done
Unscientific answered 3/8, 2016 at 13:31 Comment(0)
D
0

If the input is to be read from file (as asked here: read line reverse from a file), I created the following bash script that prints words of each line in reverse without changing the order of lines of the file (I needed this e.g. when reading words RTL instead of LTR in say chinese, etc):

#!/bin/bash

fileName="$1"
outputFile="ReversedFile.npp"

echo > ${outputFile}
lineCount=`cat $fileName | wc -l`
for ((lineNum=1; ${lineNum} <= ${lineCount}; lineNum++))
do
    echo
    echo "Processing Line ${lineNum} of ${lineCount} ..."

    lineOfFile="`cat $fileName | head -${lineNum} | tail -1`"
    echo "${lineOfFile}"

    rm -f wordFile.txt
    for eachWord in `echo "${lineOfFile}"`
    do
        echo "${eachWord}" >> wordFile.txt
    done

    if [ -e wordFile.txt ]; then
        thisLine=""
        wordCount=`cat wordFile.txt| wc -l`
        for ((wordNum=${wordCount}; ${wordNum}>=1; wordNum--))
        do
            wordOfLine="`cat wordFile.txt | head -${wordNum} | tail -1`"
            echo "${wordNum} of ${wordCount} is ${wordOfLine}"
            thisLine+="${wordOfLine} "
        done
        echo "output"
        echo "${thisLine}"
        echo "${thisLine}" >> ${outputFile}
        rm -f wordFile.txt
    fi
done

echo
echo "Output in File ${outputFile}"


Notes:
1) Make sure the input file is in UNIX EOL format otherwise last word might be truncated from each line
2) Some error checks might have been omitted for sake of simplicity

Diuresis answered 4/9, 2016 at 12:0 Comment(0)
V
0

BASH

This is the function that I use, (not tested in ash)

#reverse_word_order
function rwo(){ tr ' ' '\n'<<<"$@"|tac|tr '\n' ' ';}
echo $(rwo 'Hello There I am You')
#based on https://mcmap.net/q/478333/-how-to-reverse-a-list-of-words-in-a-shell-string

DASH

(also works in bash, but some people seem to have an aversion to echo)

rwo(){ echo "$*"|tr ' ' '\n'|tac|tr '\n' ' ';}

As 0andriy hinted at, we can golf this, (or if your platform has tac but not tr):

rwo(){ echo "$*"|tac -s " ";} #dash compatible 
rwo(){ tac -s " "<<<"$@";} #bashism
Vanegas answered 17/10, 2017 at 17:6 Comment(1)
Useless use of tr? tac -s “ “ ...Periclean
K
0

This is a slight adaptation of the Reverse Characters of Lines script in the GNU sed manual:

#!/usr/bin/sed -f

# Skip lines without blanks
/[[:blank:]]/! b

# Embed line between newlines
s/^.*$/\
&\
/

# Reset the t flag
tx

# Label to jump to
:x

# Swap first and last word between newlines for three or more words
s/\n\([^[:blank:]][^[:blank:]]*\) \(.*\) \([^[:blank:]][^[:blank:]]*\)\n/\3 \
\2\
 \1/

# Jump to label if there was a change on the line
tx

# Swap first and last word between newlines for exactly two words
s/\n\([^[:blank:]][^[:blank:]]*\) \([^[:blank:]][^[:blank:]]*\)\n/\2\
 \
\1/

# Remove the newline markers
s/\n//g

It makes a few assumptions such as no leading or trailing blanks and all the words being separated by exactly one space. Lines with leading or trailing blanks stay untouched, and lines where the words aren't all separated by exactly one space leave the extra spaces where they are, which might not be what is desired.

The script should work with any POSIX conformant sed and basic regular expressions (BRE). Using GNU sed and extended regular expressions (ERE) would allow for better readability, such as

s/\n(\S+) (.*) (\S+)\n/\3 \n\2\n \1/

for the main substitution, but if you're using sed for this problem in the first place, readability is probably not the top concern.

Kristeenkristel answered 20/7, 2018 at 15:27 Comment(0)
T
0
echo "aaaa eeee bbbb ffff cccc" | xargs echo | xargs -n1 echo | tac | xargs echo 

I use xargs for almost everything. it can put each word on a line with -n1 and it can but all the words back onto a single line without it

  • unquote it by echoing the string so the next xargs sees each word as a word
  • echo each word onto its own line
  • reverse the order of the lines
  • print them all on a single line again

tac probably has arguments that can replace some of these steps

Tobacco answered 13/7, 2021 at 8:12 Comment(0)
P
0

Reverses string but keeps endmark in place.

#!/bin/bash

#set -x

read -p "Enter a sentence: " sentence

myArray=( $sentence )

counter="${#myArray[@]}"

i=0

for word in "${myArray[@]}"
do
        if [ $counter -lt 1 ]
        then break
        fi

reverseArray[$i]="${myArray[$counter-1]}"
 
letterCounter=0
 
while read -n 1 letter
do
if [ "$letter" == '?' ] || [ "$letter" == '!' ] || [ "$letter" == '.' ]
then 
endmark="$letter"
reverseArray[$i]=$(echo "${reverseArray[$i]}" | head -c "$letterCounter")
fi
((letterCounter++))
done < <(echo "${reverseArray[$i]}")

((i++))
((counter--))
done

echo "${reverseArray[*]}$endmark"

exit

INPUT: hello world?

OUTPUT: world hello?

Postcard answered 6/2, 2022 at 3:11 Comment(0)
S
0

For example, if the input is "BASH is good" the output should be HSAB si doog

echo "Enter a String"
read text
#METHOD 1
echo -e "\n$text" | re

One more method of reversing a string is given below

echo "Enter a String"
read text
#METHOD 2
i=$((length-1))
while [ $i -ge 0 ]
do
    revstr=$revstr${text:$i:1}
    i=$((i-1))

done
echo "$revstr"

If you want to print output in separate lines you can add follwing to the echo statement :

#here "| fmt -w1" prints data in serparate lines
echo "$revstr" | fmt -w1
Scorpio answered 3/5, 2022 at 9:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.