7

I have a CSV file such as:

input.csv
1,2,3,10
4,5,6
7,8,9,12,28,30

I want to reverse the columns in this file which means:

output.csv
10,3,2,1
6,5,4
30,28,12,9,8,7

I know how to do it for a fixed column count, but if the column count varies, what should I do?

10

With perl, assuming the fields in your CSV don't have embedded commas, newlines, etc.:

perl -lpe '$_ = join ",", reverse split /,/' input.csv
1
  • 1
    The safe way would use Text::CSV, for if the assumption fails.
    – OrangeDog
    Dec 9 '16 at 16:50
9

CSV can't always be processed by simply splitting by lines and then commas, since fields may sometimes have commas or newlines themselves. To be able to include those characters, fields must be quoted.

This is a simple solution you can call from the shell that uses a proper csv parser:

ruby -r csv -e 'CSV.filter(&:reverse!)' input.csv

If you don't have ruby, this works with both python 2 and 3.

python -c $'import csv; import sys\nfor r in csv.reader(sys.stdin): r.reverse(); csv.writer(sys.stdout).writerow(r)' < input.csv > output.csv

Here it is in multiple lines:

python < input.csv > output.csv -c '
import csv
import sys

for r in csv.reader(sys.stdin):
  r.reverse()
  csv.writer(sys.stdout).writerow(r)
'

Here are some examples where this will work where solutions with simple comma splitting won't work:

input.csv

1,"2,3"

output.csv

"2,3",1

input.csv

1,"
2"

output.csv

"
2",1
1
  • i ran ruby and i liked it. mac os 11.2.2 ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin20]).
    – WEBjuju
    Mar 7 at 6:56
8

You could use awk:

awk  'BEGIN{
  FS=OFS=","                       # set the field delimiter
}
{                                  # execute only on non empty line
  for(i=NF;i>=1;i--)               # loop through all elements
    printf "%s", $i (i==1?ORS:OFS) # print the parameter together with the comma or newline 
}' input.csv
2
perl -l -a -F, -n -e 'print join ("," , reverse  @F)'  input.csv
2
awk -F, '{for(i=NF;i>=1;i--)printf("%s,",$i);printf "\n"}' input.txt | sed "s/$,//"
1

Using GNU datamash:

$ datamash -t',' reverse --no-strict <infile
10,3,2,1
6,5,4
30,28,12,9,8,7

from man datamash:

-t, --field-separator=X
    use X instead of TAB as field delimiter

reverse
    reverse field order in each line

--no-strict
    allow lines with varying number of fields

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.