开发者

How to switch/rotate every two lines with sed/awk?

开发者 https://www.devze.com 2023-03-23 06:22 出处:网络
I have been doing this by hand and I just can\'t do it anymore-- I have thousands of lines and I think this is a job for sed or awk.

I have been doing this by hand and I just can't do it anymore-- I have thousands of lines and I think this is a job for sed or awk.

Essentially, we have a file like this:

A sentence X
A matching sentence Y
A sentence Z
A matching sentence N

This pattern continues for the entire file. I want to flip every sentence and matching sentence so the entire file will end up like:

A matching sentence Y
A sentence X
A matching sentence N
A sentence Z

Any tips?

edit: extending the initial problem

Dimitre Radoulov provided a great answer for the initial problem. This is an extension开发者_开发问答 of the main problem-- some more details:

Let's say we have an organized file (due to the sed line Dimitre gave, the file is organized). However, now I want to organize the file alphabetically but only using the language (English) of the second line.

watashi 
me
annyonghaseyo
hello
dobroye utro!
Good morning!

I would like to organize alphabetically via the English sentences (every 2nd sentence). Given the above input, this should be the output:

dobroye utro!
Good morning!
annyonghaseyo
hello
watashi
me 


For the first part of the question, here is a one way to swap every other line with each other in sed without using regular expressions:

sed -n 'h;n;p;g;p'

The -n command line suppresses the automatic printing. Command h puts copies the current line from the pattern space to the hold space, n reads in the next line to the pattern space and p prints it; g copies the first line from the hold space back to the pattern space, bringing the first line back into the pattern space, and p prints it.


sed 'N; 
s/\(.*\)\n\(.*\)/\2\
\1/' infile

N - append the next line of input into the pattern space
\(.*\)\n\(.*\) - save the matching parts of the pattern space the one before and the one after the newline.
\2\\ \1 - exchange the two lines (\1 is the first saved part, \2 the second). Use escaped literal newline for portability

With some sed implementations you could use the escape sequence \n: \2\n\1 instead.


First question:

awk '{x = $0; getline; print; print x}' filename

next question: sort by 2nd line

paste - - < filename | sort -f -t $'\t' -k 2 | tr '\t' '\n'

which outputs:

dobroye utro!
Good morning!
annyonghaseyo
hello
watashi
me


Assuming an input file like this:

A sentence X
Z matching sentence Y
A sentence Z
B matching sentence N
A sentence Z
M matching sentence N

You could do both exchange and sort with Perl:

perl -lne'
 $_{ $_ } = $v unless $. % 2;
 $v = $_;
 END {
  print $_, $/, $_{ $_ }
    for sort keys %_; 
   }' infile

The output I get is:

% perl -lne'
 $_{ $_ } = $v unless $. % 2;
 $v = $_;
 END {
  print $_, $/, $_{ $_ }
    for sort keys %_;
   }' infile
B matching sentence N
A sentence Z
M matching sentence N
A sentence Z
Z matching sentence Y
A sentence X

If you want to order by the first line (before the exchange):

perl -lne'
 $_{ $_ } = $v unless $. % 2;
 $v = $_;
 END {
  print $_, $/, $_{ $_ }
    for sort {
      $_{ $a } cmp $_{ $b }
      } keys %_; 
   }' infile

So, if the original file looks like this:

% cat infile1
me
watashi 
hello
annyonghaseyo
Good morning!
dobroye utro!

The output should look like this:

% perl -lne'
 $_{ $_ } = $v unless $. % 2;
 $v = $_;
 END {
  print $_, $/, $_{ $_ }
    for sort {
  $_{ $a } cmp $_{ $b }
  } keys %_;
   }' infile1
dobroye utro!
Good morning!
annyonghaseyo
hello
watashi 
me

This version should handle duplicate records correctly:

perl -lne'
 $_{ $_, $. } = $v unless $. % 2;
 $v = $_;
 END {
    print substr( $_, 0, length() - 1) , $/, $_{ $_ }
    for sort {
      $_{ $a } cmp $_{ $b }
      } keys %_; 
   }' infile

And another version, inspired by the solution posted by Glenn (record exchange included and assuming the pattern _ZZ_ is not present in the text file):

sed 'N; 
  s/\(.*\)\n\(.*\)/\1_ZZ_\2/' infile | 
    sort |
      sed 's/\(.*\)_ZZ_\(.*\)/\2\
\1/'
0

精彩评论

暂无评论...
验证码 换一张
取 消