更改文件中的行顺序


11

我正在尝试更改特定模式下的行顺序。使用包含多行(例如99行)的文件。对于每三行,我希望第二行是第三行,第三行是第二行。

例。

1-输入:

gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...

2-输出:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...

Answers:


12

使用awk和整数数学:

awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay } }' /path/to/input

模运算符执行整数除法并返回余数,因此对于每一行,它将返回序列1、2、0、1、2、0 [...]。知道了这一点,我们只是将输入保存在模数为2的行上,以备以后使用,也就是在输入为零时打印输出。


我们这里有一个小缺陷。看到我的答案,小改进部分
Sergiy Kolodyazhnyy

谢谢你的好收获;我已将修复程序以的形式合并到我的答案中NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }
DopeGhoti

23
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

就是说,p使当前行变粗,获得前一个n扩展名,h使其变旧,获得最前n一个扩展名,G然后保留行(将其追加到模式空间),并p用两行第三行和第二行交换该行的模式空间。


3

另一种awk方法:

awk '{print $0; if ((getline L2)>0 && (getline L3)>0){ print L3 ORS L2 }}' file

输出:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

  • (getline L2)>0 && (getline L3)>0-提取接下来的2条记录(如果存在)

  • 每个第二和第三条记录分别分配给L2L3变量


1
我假设这些变量以字母L(小写)开头。对于可读性来说,它们是不好的选择,因为它们看起来像十二和十三的数字。一个更好的选择可能是line2,等
已暂停,直至另行通知。

@DennisWilliamson,改为大写
RomanPerekhrest

1

使用perl和一个简短的脚本:

user@pc:~$ cat input.txt 
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

user@pc:~$ perl -ne '$l2=<>; $l3=<>; print $_,$l3,$l2;' input.txt 
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

该脚本会处理整个文件,对于每一行(存储在中$_),它将获取接下来的两行($l2$l3),并按照请求的顺序打印它们:line1,line3,line2。


1

一种方法可能如下:

sed -e '
   /\n/s/\(.*\)\(\n\)\(.*\)/\3\2\1/;//b
   $!N;$q;N;                            # load up the pattern space with 3 lines provided eof not reached
   P;D;                                 # first just print the first line then interchange the two and print them
' yourfile

或者,

perl -ne 'print $_, reverse scalar <>, scalar <>' yourfile

结果

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

1

为什么不做一会儿循环呢?展开形式:

( while read a
  do
    read b
    read c
    echo "$a"
    echo "$c"
    echo "$b"
  done
) < input.txt

在“单行格式”中:

( while read a ; do read b ; read c ; echo "$a" ; echo "$c" ; echo "$b" ; done) < input.txt

输出:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

1

佩尔

perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt

这里的想法是,我们使用%带有行号$.变量的模运算符,以找出哪个是第一行,哪个是第二行,哪个是第三行。对于第三行,余数为0,而对于第一行和第二行,它将有相应的数字。

测试:

$ cat input.txt                                                                                                          
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt                                    
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

小改进

将第二行存储到变量中的方法存在缺陷。如果最后一行是“第二”行,即该行号的余数是2怎么办?My dog is orange如果我们省略最后一行,我和DopeGhoti的答案中的原始代码将不会打印。两种情况下的解决方案都是使用END{}代码块,并在打印后取消设置临时变量。换一种说法:

$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt 

这样,代码将适用于文件中任意数量的行,而不仅仅是被3整除的行。

评论中提到的问题的其他修复

在awk的情况下,如果文件的最后一行产生$的输出1。%3,由于无条件打印,前面的代码存在输出空白换行符的问题END{print delay},因为print注释中提到的函数始终将换行符附加到要操作的任何变量上。如果是perl版本,则不会发生此问题,因为带有-ne标志print功能不会附加换行符。

尽管如此,awk的解决方法是使条件语句成为条件,正如Dope Ghoti在评论中所提到的那样,是为了验证临时变量的长度。相同修复程序的perl版本为:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 

1
您的修复程序本身就有潜在的小缺陷,因为它将为行数“错误”的文件添加空白输出行。我已将(for awk)的答复纳入您的改进中,从而解决了此问题NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }
DopeGhoti

1
@DopeGhoti perl不会发生此问题,因为perl的带有-ne标志的打印不会输出换行符。它确实可以打印,但是它是一个空字符串,没有尾随换行符。但是,我在回答中添加了对该问题的提及和相同的解决方法。谢谢 !
Sergiy Kolodyazhnyy

1

Vim

不适合用于长文件,但是如果您只是在编辑文件并想要例如对某些Yaml节重新排序,则仍然很方便。

首先记录一个宏:

gg qq j ddp j q

然后重复所需的次数:

@q @q @q ...

或者只是

3@q

说明:

  • gg-转到第一行
  • qq-开始录制宏
  • j-转到第二行
  • ddp-交换第二行和第三行
  • j-转到第四行,即转到下三行的第一行
  • q-停止录制
  • @q-重播一次宏
  • 3 @ q-重播宏3次

1
代替手动重复@q @q @q,可以通过这种方式进行3@q-重复三遍。100@q-重复宏100次。
MiniMax

0

用法: ./shuffle_lines.awk input.txt

检查shebang #!/usr/bin/awk -f,因为awk系统上的位置可能不同。

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.