根据 bash 前后的行处理一行
Process a line based on lines before and after in bash
我想弄清楚如何编写一个 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
$ awk 'minus2==[=10=]{minus1=[=10=]} NR>1{print minus1} {minus2=minus1; minus1=[=10=]} END{print minus1}' file
3
1
1
1
2
2
2
2
1
1
1
2
2
2
2
工作原理
minus2==[=12=]{minus1=[=12=]}
如果2行前的行与当前行相同,则将1行前的行设置为当前行。
NR>1{print minus1}
如果我们超过了第一行,则打印从 1 行之前开始的行。
minus2=minus1; minus1=[=14=]
更新变量。
END{print minus1}
我们读完文件后,打印最后一行。
多行版本
对于那些喜欢他们的代码分布在多行中的人:
awk '
minus2==[=11=]{
minus1=[=11=]
}
NR>1{
print minus1
}
{
minus2=minus1
minus1=[=11=]
}
END{
print minus1
}
' file
这是一个 (GNU) sed 解决方案:
$ sed -r '1N;N;/^(.*)\n.*\n$/s/^(.*\n).*\n//;P;D' infile
3
1
1
1
2
2
2
2
1
1
1
2
2
2
2
这适用于移动的三行 window。更具可读性:
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$/s/^(.*\n).*\n// # See below
P # Print first line of pattern space
D # Delete first line of pattern space
' infile
N;P;D
是获得移动两行的惯用方法 window:追加一行,打印第一行,删除模式的第一行 space。为了获得移动的三行 window,我们读取额外的一行,但只读取一次,即在处理第一行时 (1N
).
复杂的位是检查模式 space 的第一行和第三行是否相同,如果相同,则用第一行替换第二行。为了检查我们是否必须进行替换,我们使用地址
/^(.*)\n.*\n$/
锚点 ^
和 $
并不是真正需要的,因为我们总是在模式 space 中使用换行符,但它更清楚地表明我们想要匹配完整的模式 space。我们将第一行放入捕获组中,并通过使用反向引用查看它是否在第三行重复。
然后,如果是这种情况,我们执行替换
s/^(.*\n).*\n//
这会捕获包含换行符的第一行,匹配包含换行符的第二行,并替换为第一行的两倍。 P
和 D
然后打印并删除第一行。
当到达结尾时,整个模式 space 被打印出来,所以我们没有吞下任何行。
这也适用于第二个输入示例:
$ sed -r '1N;N;/^(.*)\n.*\n$/s/^(.*\n).*\n//;P;D' infile2
2
2
2
2
2
2
要与 BSD sed 一起使用(在 OS X 中找到),您要么必须使用 -E
而不是 -r
选项,要么不使用任何选项,即,基本正则表达式并转义捕获组中的所有括号 (\(\)
)。换行匹配应该可以,但我没有测试。如有疑问,请检查 this great answer 列出所有差异。
我想弄清楚如何编写一个 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
$ awk 'minus2==[=10=]{minus1=[=10=]} NR>1{print minus1} {minus2=minus1; minus1=[=10=]} END{print minus1}' file
3
1
1
1
2
2
2
2
1
1
1
2
2
2
2
工作原理
minus2==[=12=]{minus1=[=12=]}
如果2行前的行与当前行相同,则将1行前的行设置为当前行。
NR>1{print minus1}
如果我们超过了第一行,则打印从 1 行之前开始的行。
minus2=minus1; minus1=[=14=]
更新变量。
END{print minus1}
我们读完文件后,打印最后一行。
多行版本
对于那些喜欢他们的代码分布在多行中的人:
awk '
minus2==[=11=]{
minus1=[=11=]
}
NR>1{
print minus1
}
{
minus2=minus1
minus1=[=11=]
}
END{
print minus1
}
' file
这是一个 (GNU) sed 解决方案:
$ sed -r '1N;N;/^(.*)\n.*\n$/s/^(.*\n).*\n//;P;D' infile
3
1
1
1
2
2
2
2
1
1
1
2
2
2
2
这适用于移动的三行 window。更具可读性:
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$/s/^(.*\n).*\n// # See below
P # Print first line of pattern space
D # Delete first line of pattern space
' infile
N;P;D
是获得移动两行的惯用方法 window:追加一行,打印第一行,删除模式的第一行 space。为了获得移动的三行 window,我们读取额外的一行,但只读取一次,即在处理第一行时 (1N
).
复杂的位是检查模式 space 的第一行和第三行是否相同,如果相同,则用第一行替换第二行。为了检查我们是否必须进行替换,我们使用地址
/^(.*)\n.*\n$/
锚点 ^
和 $
并不是真正需要的,因为我们总是在模式 space 中使用换行符,但它更清楚地表明我们想要匹配完整的模式 space。我们将第一行放入捕获组中,并通过使用反向引用查看它是否在第三行重复。
然后,如果是这种情况,我们执行替换
s/^(.*\n).*\n//
这会捕获包含换行符的第一行,匹配包含换行符的第二行,并替换为第一行的两倍。 P
和 D
然后打印并删除第一行。
当到达结尾时,整个模式 space 被打印出来,所以我们没有吞下任何行。
这也适用于第二个输入示例:
$ sed -r '1N;N;/^(.*)\n.*\n$/s/^(.*\n).*\n//;P;D' infile2
2
2
2
2
2
2
要与 BSD sed 一起使用(在 OS X 中找到),您要么必须使用 -E
而不是 -r
选项,要么不使用任何选项,即,基本正则表达式并转义捕获组中的所有括号 (\(\)
)。换行匹配应该可以,但我没有测试。如有疑问,请检查 this great answer 列出所有差异。