Dejwi
Dejwi

Reputation: 4487

bash reverse order of comma separated values

I've got the following list of elements:

a, b, c, 1337, d, e

I wish I have:

e, d, 1337, c, b, a

How can I achieve that in bash?

Upvotes: 7

Views: 6355

Answers (6)

sampson-chen
sampson-chen

Reputation: 47327

You can do this with awk:

#!/bin/bash

awk 'BEGIN{FS=",[ ]*"; OFS=", "}
    {
        for (i=NF; i>0; i--) {
            printf "%s", $i;
            if (i>1) {
                printf "%s", OFS;
            }
        }
        printf "\n"
    }'

Explanation of the script:

  • FS=",[ ]*";: The regex for Field Separator (delimiter for your input) matches a comma followed by zero or more spaces, so your input can be any of:
    • a, b, c, 1337, d, e
    • a,b,c,1337,d,e
    • a, (many spaces) b, c,1337,d, e
  • OFS=", ": The Output Field Separator (delimiter for your output) will be explicitly a comma followed by a space (so output looks consistent)
  • for (i=NF; i>0; i--) { ... }: NF means the Number of Fields in the current line. Here we iterate backwards, printing from the last field to the first field.
  • if (i>1) { ... }: Only print the OFS if it's not the last field in the output
  • printf "\n": new line.

Sample usage:

$ ./script_name < input_file > output_file

Upvotes: 5

glenn jackman
glenn jackman

Reputation: 247042

bash:

echo "a, b, c, 1337, d, e" | (
  IFS=", "
  while read line; do
    set -- $line
    for (( i=$#; i > 1; i-- )); do
      printf "%s, " "${!i}"
    done
    echo "$1"
  done
)

I use parentheses to create a subshell so I don't alter the IFS variable in the current shell.

Upvotes: 1

dinox0r
dinox0r

Reputation: 16039

With the following script, it doesn't matter how many elements are in the list:

IFS=', '; read -a array <<< "a, b, c, 1337, d, e"
let i=${#array[@]}-1; 
while [ $i -ge 0 ]; do 
    echo -n "${array[$i]}, "; 
    let i--; 
done

Also, you can use the following perl one-liner (it will add a \n after the first element though)

echo "a, b, c, 1337, d, e" | perl -ne 'foreach(reverse(split(", ",$_))){print "$_, "}'

Upvotes: 1

Julien Vivenot
Julien Vivenot

Reputation: 2250

$ awk -F", " '{for (i=NF;i;i--) printf "%s ",$i; print ""}' < datafile
e d 1337 c b a 

Upvotes: 2

Lev Levitsky
Lev Levitsky

Reputation: 65821

This solution is not very elegant, but should work with any number of fields:

sed 's/, /\n/g' | tac | sed ':a;$!{N;ba};s/\n/, /g'

Either pipe you data to it or give a file as second argument to first sed (it will print the whole file as one comma-separated line, though).

Upvotes: 2

newfurniturey
newfurniturey

Reputation: 38456

If your input is simple, and the number of columns is static, you could always go with a hardcoded awk solution:

awk -F, '{ print $6", "$5", "$4", "$3", "$2", "$1 }'

This assumes that you have 6 columns that are separated by a comma. If you have a "comma + space" delimiter, ,, you can use -F', ' instead of just -F,.

A straightforward example usage with output:

$ echo "a, b, c, 1337, d, e" | awk -F', ' '{ print $6", "$5", "$4", "$3", "$2", "$1 }'
e, d, 1337, c, b, a

With a file as input:

awk -F', ' '{ print $6", "$5", "$4", "$3", "$2", "$1 }' your_file

You can then redirect the output from that to another file by appending > reversed_columns; "reversed_columns" being the name of the new file in this case.

Upvotes: 2

Related Questions