Reading text file, change order of columns of some

2019-08-30 02:30发布

问题:

My input file is of the form:

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0 3/4     1     0     0 1/4     0     0    -1 1/2
   0    -1     0 1/4    -1     0     0 3/4     0     0     1 1/2

I want to rearrange the order of the lines that have the fraction within them. Currently I have:

#!bin/bash
filename="input.txt"
while ((i++)); read -r line; do
  re='[0-9][/][0-9]';
  if [[ $line =~ $re ]]
    then
      echo $line
  fi
done < "$filename"

which will echo the second and third line. Is there an awk or sed command I could use to get these two lines to change their order (leaving the first as is) to being

$1,$2,$3,$5,$6,$7,$9,$10,$11,$4,$8,$12

which would make my file now look like

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0     1     0     0     0     0    -1 3/4 1/4 1/2
   0    -1     0    -1     0     0     0     0     1 1/4 3/4 1/2

回答1:

you can easily do this with awk, nonetheless I think it is important to define the rules of the game a bit. Under the following assumptions :

  • A fraction is anything of the form : a/b or a / b or a/ b
  • If a fraction appears in column 4 or 8, reshuffle the columns.
  • you want to keep the formatting correct

With this in mind, you can use the following awk code

awk 'BEGIN{format="%4s%6s%6s%6s%6s%6s%6s%6s%6s%7s%7s%7s\n"}
     { gsub(/[[:blank:]]*\/[[:blank:]]*/,"/",$0); $0=$0 }
     ($4 ~ /\//) || ($8 ~ /\//) { 
        $12=$4" "$8" "$12
        $4=""; $8=""
        $0=$0
     }                                           
     { printf format,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12 }
    ' file.txt

This does the following :

  • replace all " / " or any variant on that by a single /

  • $0 = $0 redefines the fields, i.e. in your first two lines, you will move from 18 fields to 12

  • if a fraction (i.e. a /) appears in field 4 or 8, then redefine field 12, delete field 4 and 8 and do $0=$0 again.

  • print with the correct format.

NOTE: in the above example, the fractions have a different output (no spaces)

The above will give you the following output :

   0     1     0     0     0     1     1     0     0    0/1    0/1    0/1
   0     1     0     1     0     0     0     0    -1    3/4    1/4    1/2
   0    -1     0    -1     0     0     0     0     1    1/4    3/4    1/2

If you do not want change your fractions in the first line, then you can do it very easy like this

awk 'BEGIN{format="%4s%6s%6s%6s%6s%6s%6s%6s%6s%7s%7s%7s\n"}
     (NF>12) { print; next }
     { 
        $12=$4" "$8" "$12
        $4=""; $8=""
        $0=$0
        printf format,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12
     }
    ' file.txt

Here you assume, that

  • if a line has more then 12 fields, just print it

  • otherwise, shuffle the columns

This is however less robust as everything depends how the fractions are typed in the 4th,8th and 12th column. I.e. they must be typed without spaces. Output will look like :

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0     1     0     0     0     0    -1    3/4    1/4    1/2
   0    -1     0    -1     0     0     0     0     1    1/4    3/4    1/2


回答2:

This is better to be done using awk:

awk -v OFS='\t' '/[0-9]\/[0-9]/{print $1,$2,$3,$5,$6,$7,$9,$10,$11,$4,$8,$12; next} 1' file

0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
0   1   0   1   0   0   0   0   -1  3/4 1/4 1/2
0   -1  0   -1  0   0   0   0   1   1/4 3/4 1/2


回答3:

@anubhava is a better solution than me. Since I wrote the other code, mind as well.

#!/bin/bash

filename="input.txt"

awk '
{
for (i=1; i <= NF; i++)
  if ( $(i+1) == "/" || $i == "/" || $(i-1) == "/") {
    printf "MM%sMM",$i" "$(i+1)" "$(i+2)
    i = i+2
  } else if ( match ($i, /^[[:digit:]]\/[[:digit:]]/) ) {
    printf "MM%sMM",$i
  } else {
    printf "MM%sMM",$i
  }
  printf "\n"
}' $filename | sed -e 's/MMMM/MM/g;s/^MM//;s/MM/\t/g' 


标签: bash shell awk sed