reverse the order of characters in a string

Bash

Bash Problem Overview


In string "12345", out string "54321". Preferably without third party tools and regex.

Bash Solutions


Solution 1 - Bash

I know you said "without third-party tools", but sometimes a tool is just too obviously the right one, plus it's installed on most Linux systems by default:

[madhatta@risby tmp]$ echo 12345 | rev
54321

See rev's man page for more.

Solution 2 - Bash

Simple:

var="12345"
copy=${var}

len=${#copy}
for((i=$len-1;i>=0;i--)); do rev="$rev${copy:$i:1}"; done

echo "var: $var, rev: $rev"

Output:

$ bash rev
var: 12345, rev: 54321

Solution 3 - Bash

Presume that a variable 'var' has the value '123'

var="123"

Reverse the string and store in a new variable 'rav':

rav=$(echo $var | rev)

You'll see the 'rav' has the value of '321' using echo.

echo $rav

Solution 4 - Bash

rev | tail -r (BSD) or rev | tac (GNU) also reverse lines:

$ rev <<< $'12\n34' | tail -r
43
21
$ rev <<< $'12\n34' | gtac
43
21

If LC_CTYPE is C, rev reverses the bytes of multibyte characters:

$ LC_CTYPE=C rev <<< あの
��め�
$ export LC_ALL=C; LC_ALL=en_US.UTF-8 rev <<< あの
のあ

Solution 5 - Bash

A bash solution improving over @osdyng answer (my edit was not accepted):

var="12345"     rev=""

for(( i=0 ; i<${#var} ; i++ )); do rev="${var:i:1}$rev"; done

echo "var: $var, rev: $rev"

Or an even simpler (bash) loop:

var=$1   len="${#var}"   i=0   rev=""

while (( i<len )); do rev="${var:i++:1}$rev"; done

echo "var: $var, rev: $rev"

A POSIX solution:

var="12345"     rev=""    i=1

while  [ "$i" -le "${#var}" ]
do     rev="$(echo "$var" | awk -v i="$i" '{print(substr($0,i,1))}')$rev"
       : $(( i+=1 ))
done

echo "var: $var, rev: $rev"

Note: This works on multi byte strings. Cut solutions will work only in ASCII (1 byte) strings.

Solution 6 - Bash

This reverses the string "in place":

a=12345
len=${#a}
for ((i=1;i<len;i++)); do a=$a${a: -i*2:1}; done; a=${a:len-1}
echo $a

or the third line could be:

for ((i=0;i<len;i++)); do a=${a:i*2:1}$a; done; a=${a:0:len}

or

for ((i=1;i<len;i++)); do a=${a:0:len-i-1}${a: -i:i+1}${a:len-i-1:1}; done

Solution 7 - Bash

For those without rev (recommended), there is the following simple awk solution that splits fields on the null string (every character is a separate field) and prints in reverse:

awk -F '' '{ for(i=NF; i; i--) printf("%c", $i); print "" }'

The above awk code is POSIX compliant. As a compliant awk implementation is guaranteed to be on every POSIX compliant OS, the solution should thus not be thought of as "3rd-party." This code will likely be more concise and understandable than a pure POSIX sh (or bash) solution.

(; I do not know if you consider the null string to -F a regex... ;)

Solution 8 - Bash

Some simple methods of reversing a string

echo '!!!esreveR si sihT' | grep -o . | tac | tr -d '\n' ; echo

echo '!!!esreveR si sihT' | fold -w 1 | tac | tr -d '\n' ; echo

Convert to hex values then reverse

echo '!!!esreveR si sihT' | xxd -p | grep -o .. | tac | xxd -r -p ; echo

echo '!!!esreveR si sihT' | xxd -p | fold -w 2 | tac | xxd -r -p ; echo

Solution 9 - Bash

If var=12345:

[tag:bash] for((i=0;i<${#var};i++)); do rev="$rev${var:~i:1}"; done

[tag:sh] c=$var; while [ "$c" ]; do rev=$rev${c#"${c%?}"}; c=${c%?}; done

echo "var: $var, rev: $rev"

Run it:

$ rev
var: 12345, rev: 54321

Solution 10 - Bash

This can of course be shortened, but it should be simple to understand: the final print adds the newline.

echo 12345 | awk '{for (i = length($0); i > 0; i--) {printf("%s", substr($0, i, 1));} print "";}'

Solution 11 - Bash

Nobody appears to have posted a sed solution, so here's one that works in non-GNU sed (so I wouldn't consider it "3rd party"). It does capture single characters using the regex ., but that's the only regex.

In two stages:

$ echo 123456 | sed $'s/./&\\\n/g' | sed -ne $'x;H;${x;s/\\n//g;p;}'
654321

This uses bash format substitution to include newlines in the scripts (since the question is tagged [tag:bash]). It works by first separating the input string into one line per character, and then by inserting each character into the beginning of the hold buffer.

  • x swaps the hold space and the pattern space, and
  • H H appends the (current) pattern space to the hold space.

So for every character, we place that character into the hold space, then append the old hold space to it, thus reversing the input. The final command removes the newlines in order to reconstruct the original string.

This should work for any single string, but it will concatenate multi-line input into a single output string.

Solution 12 - Bash

Here is another simpler awk solution:

awk 'BEGIN{FS=""} {for (i=NF; i>0; i--) s=s $i; print s}' <<< '123456'

654321

Solution 13 - Bash

read word

reve=`echo "$word" | awk '{for(i=length($0); i>0;i--) printf (substr($0,i,1));}'`
echo  "$reve"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbindbnView Question on Stackoverflow
Solution 1 - BashMadHatterView Answer on Stackoverflow
Solution 2 - BashosdyngView Answer on Stackoverflow
Solution 3 - BashNickView Answer on Stackoverflow
Solution 4 - BashLriView Answer on Stackoverflow
Solution 5 - Bashuser2350426View Answer on Stackoverflow
Solution 6 - BashDennis WilliamsonView Answer on Stackoverflow
Solution 7 - BashMichael BackView Answer on Stackoverflow
Solution 8 - BashDavid HeffernanView Answer on Stackoverflow
Solution 9 - BashdoneView Answer on Stackoverflow
Solution 10 - BashNonoView Answer on Stackoverflow
Solution 11 - BashghotiView Answer on Stackoverflow
Solution 12 - BashanubhavaView Answer on Stackoverflow
Solution 13 - BashNikhilView Answer on Stackoverflow