31

I would like to sort the characters in a string.

E.g.

echo cba | sort-command
abc

Is there a command that will allow me to do this or will I have to write an awk script to iterate over the string and sort it?

1
  • Why can't this be done the way described above? Isn't sort able to take stdin as an input? Commented Sep 4, 2018 at 22:04

6 Answers 6

51
echo cba | grep -o . | sort |tr -d "\n"
Sign up to request clarification or add additional context in comments.

2 Comments

grep -o . is a cheap way to each character on its own line before using sort
Since this is already using grep, it's also easy to adjust to match only some characters. E.g. when you don't want to include whitespace in the result, grep -o '\S'.
16

Please find the following useful methods:

Shell

Sort string based on its characters:

echo cba | grep -o . | sort | tr -d "\n"

String separated by spaces:

echo 'dd aa cc bb' | tr " " "\n" | sort | tr "\n" " "

Perl

print (join "", sort split //,$_)

Ruby

ruby -e 'puts "dd aa cc bb".split(/\s+/).sort' 

Bash

With bash you have to enumerate each character from a string, in general something like:

str="dd aa cc bb";
for (( i = 0; i < ${#str[@]}; i++ )); do echo "${str[$i]}"; done

For sorting array, please check: How to sort an array in bash?

1 Comment

Far as I can tell the last example for Bash doesn't work as written, "in general something like" qualifier or not. Correct syntax to achieve the goal would be: for (( i = 0; i < ${#str}; i++ )); do echo "${str:${i}:1}" ; done Problem with the original suggestion is that str="dd aa cc bb" does NOT create an array, just a string of characters, and the suggested for loop doesn't step through that string. If the goal is to take in a string and output its contents one character at a time, my amended for loop accomplishes this by extracting each character as a 1-char substring and echoing it.
7

This is cheating (because it uses Perl), but works. :-P

echo cba | perl -pe 'chomp; $_ = join "", sort split //'

1 Comment

or Ruby ;) echo cba | ruby -e 'puts ARGF.read.split(%r{\s*}).sort.join'
6

Another perl one-liner

$ echo cba | perl -F -lane 'print sort @F'
abc

$ # for reverse order
$ echo xyz | perl -F -lane 'print reverse sort @F'
zyx
$ # or
$ echo xyz | perl -F -lane 'print sort {$b cmp $a} @F'
zyx
  • This will add newline to output as well, courtesy -l option
  • The input is basically split character wise and saved in @F array
  • Then sorted @F is printed


This will also work line wise for given input file

$ cat ip.txt 
idea
cold
spare
umbrella

$ perl -F -lane 'print sort @F' ip.txt 
adei
cdlo
aeprs
abellmru

Comments

0

This would have been more appropriate as a comment to one of the grep -o . solutions (my reputation's not quite up to that low bar alas, damn my lurking), but I thought it worth mentioning that separating letters can be done more efficiently within the shell. It's always worth avoiding code, but this letsep function is pretty small:

letsep ()
{
INWORD="$1"
while [ "$INWORD" ]
do
    echo ${INWORD:0:1}
    INWORD=${INWORD#?}
done
}

. . . and outputs one letter per line for an input string of arbitrary length. For example, once letsep is defined, populating an array FLETRS with the letters of a string contained in variable FRED could be done (assuming contemporary bash) as:

readarray -t FLETRS < <(letsep $FRED)

. . . which for word-size strings runs about twice as fast as the equivalent :

readarray -t FLETRS < <(echo $FRED | grep -o .)

Whether this is worth setting up depends on the application. I've only measured this crudely, but the slower procedural code seems to maintain an advantage over the context switch up to ~60 chars (grep is obviously more efficient, but loading it is relatively expensive). If the above operation is taking place in one or more steps of a loop over an indeterminate number of executions, the difference in efficiency can add up (at which point some might argue for switching tools and rewriting regardless, but that's another set of tradeoffs).

Comments

0

Here is @ghostdog74's answer implemented in fish shell:

string match --all --regex . "cba" | sort | string join ""

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.