基于bash中前后的行处理行

基于bash中前后的行处理行,bash,readlines,Bash,Readlines,我试图弄清楚如何编写一个bash脚本,它使用一行前后的行作为条件。我将给出一个类似python的伪代码示例,这对我来说很有意义 基本上: for line in FILE: if line_minus_1 == line_plus_one: line = line_minus_1 最好的方法是什么 因此,如果我有一个输入文件,其内容如下: 3 1 1 1 2 2 1 2 1 1 1 2 2 1 2 我的输出是: 3 1 1 1 2 2 2 2 1 1 1 2 2 2

我试图弄清楚如何编写一个bash脚本,它使用一行前后的行作为条件。我将给出一个类似python的伪代码示例,这对我来说很有意义

基本上:

for line in FILE:
    if line_minus_1 == line_plus_one:
        line = line_minus_1
最好的方法是什么

因此,如果我有一个输入文件,其内容如下:

3
1
1
1
2
2
1
2
1
1
1
2
2
1
2
我的输出是:

3
1
1
1
2
2
2
2
1
1
1
2
2
2
2
请注意,它从第一行开始,直到最后一行,并尊重在前面几行中所做的更改,因此如果我有:

2
1
2
1
2
2
我会得到:

2
2
2
2
2
2
而不是:

2
1
1
1
2
2
工作原理
  • minus2==0{minus1=$0}

    如果2行之前的行与当前行相同,则将1行之前的行设置为当前行

  • NR>1{print minus 1}

    如果我们超过了第一行,则打印1行之前的行

  • minus2=minus1;减1=$0

    更新变量

  • END{print minus 1}

    读完文件后,打印最后一行

多行版本 对于喜欢将代码分散在多行的用户:

awk '
    minus2==$0{
        minus1=$0
    }

    NR>1{
        print minus1
    }

    {
        minus2=minus1
        minus1=$0
    }

    END{
        print minus1
    }
    ' file
以下是一个(GNU)sed解决方案:

$ sed -r '1N;N;/^(.*)\n.*\n\1$/s/^(.*\n).*\n/\1\1/;P;D' infile
3
1
1
1
2
2
2
2
1
1
1
2
2
2
2
这适用于移动的三行窗口。更具可读性:

sed -r '        # -r for extended regular expressions: () instead of \(\)
    1N          # On first line, append second line to pattern space
    N           # On all lines, append third line to pattern space
    /^(.*)\n.*\n\1$/s/^(.*\n).*\n/\1\1/    # See below
    P           # Print first line of pattern space
    D           # Delete first line of pattern space
' infile
N;PD
是获得移动的两行窗口的惯用方法:追加一行,打印第一行,删除模式空间的第一行。为了获得一个移动的三行窗口,我们读取了一个额外的行,但只读取了一次,即在处理第一行时(
1N

复杂位检查模式空间的第一行和第三行是否相同,如果相同,则用第一行替换第二行。为了检查我们是否必须进行替换,我们使用地址

/^(.*)\n.*\n\1$/
锚定
^
$
并不是真正需要的,因为我们总是在模式空间中精确地找到换行符,但它更清楚地表明我们想要匹配完整的模式空间。我们将第一行放入一个捕获组,并通过使用反向引用查看它是否在第三行上重复

然后,如果是这种情况,我们执行替换

s/^(.*\n).*\n/\1\1/
这将捕获包括换行符的第一行,匹配包括换行符的第二行,并用第一行的两倍进行替换
P
D
然后打印并删除第一行

当到达终点时,整个图案空间被打印出来,因此我们不会吞下任何线条

这也适用于第二个输入示例:

$ sed -r '1N;N;/^(.*)\n.*\n\1$/s/^(.*\n).*\n/\1\1/;P;D' infile2
2
2
2
2
2
2


要使用BSD sed(如OS X中所示),您必须使用
-E
而不是
-r
选项,或者不使用任何选项,即基本正则表达式并转义捕获组中的所有括号(
\(\)
)。换行符匹配应该有效,但我没有测试它。如果有疑问,请检查所有差异。

完全正确!awk非常棒
$ sed -r '1N;N;/^(.*)\n.*\n\1$/s/^(.*\n).*\n/\1\1/;P;D' infile2
2
2
2
2
2
2