Contents

查找匹配的文本并替换下一行

1. 概述

我们经常使用sedawk 命令在 Linux 命令行中进行“搜索和替换”作业。通常,匹配文本和替换出现在同一行中。

但是,有时,当我们在一行中找到一些模式时,我们希望在与之相关的另一行中进行替换——例如,查找匹配的文本并在下一行应用一些文本替换。

在本教程中,我们将探讨如何做到这一点。

2. 问题介绍

像往常一样,让我们通过一个例子快速理解问题。

首先,假设我们有一个输入文件:

$ cat revenue.txt
Revenue Report
==============
In 2021, we've made in total 4000 dollars' revenue.
Quarter-based revenue report is shown below:
- Quarter 1:
Revenue: 1200 dollars; Profit: 700 dollars
- Quarter 2:
Revenue: 1000 dollars; Profit: 650 dollars
- Quarter 3:
Revenue: 1200 dollars; Profit: 800 dollars
- Quarter 4:
Revenue: 600 dollars; Profit: -200 dollars
## Total
Revenue: 4000 dollars
Profit: 1950 dollars

假设我们从财务部门获得了 2021 年的收入报告。

如上面的输出所示,“dollars”这个词在文件中出现了很多次。现在,我们只想在季度报告部分将“dollars”一词替换为“ $ ”,以使内容更易于阅读。

也就是说,只有当前一行与模式‘Quarter [1-4]:’匹配时,我们才需要将单词“ dollars ”替换为“ $ ” 。

接下来,我们将讨论如何使用sedawk解决问题。此外,由于GNU awkGNU sed在 Linux 中被广泛使用,我们将在本教程的示例中使用它们。

现在,让我们看看他们的行动。

3. 使用sed命令

我们将 在本节中介绍两种sed单线解决方案。

3.1. 两个sed命令行

我们来看第一个:

$ sed '/Quarter [1-4]:/{ n; s/dollars/$/g }' revenue.txt
Revenue Report
==============
In 2021, we've made in total 4000 dollars' revenue.
Quarter-based revenue report is shown below:
- Quarter 1:
Revenue: 1200 $; Profit: 700 $
- Quarter 2:
Revenue: 1000 $; Profit: 650 $
- Quarter 3:
Revenue: 1200 $; Profit: 800 $
- Quarter 4:
Revenue: 600 $; Profit: -200 $
## Total
Revenue: 4000 dollars
Profit: 1950 dollars

如上面的sed输出所示,只有季度报告部分中出现的“dollars”被“ $ ”替换。其他出现,例如“*Total”部分中的“*dollars” ,不会更改。

因此,该命令解决了问题。sed命令也很简单。关键是’ n ‘命令。

** sed工具的“ n  ”命令将打印当前行并将下一个输入行读入模式空间 **。

因此,命令遵循以下逻辑:

  • 如果当前行匹配*/Quarter [1-4]:/* – 打印当前行并读取下一行 ( n ),然后应用替换 ( s/../../g ) 并输出结果
  • 否则 - 按原样打印当前行

或者,我们可以使用 sed的“ n ”和“ b LABEL(分支) 命令来实现我们的目标:

sed '/Quarter [1-4]:/!b; n; s/dollars/$/g' revenue.txt

敏锐的眼睛可能已经发现我们没有将LABEL传递给sed的 ’ b ’ 命令。如果’b LABEL‘的 LABEL被省略,sed将开始加载下一行。

现在,让我们了解一下这个sed命令是如何工作的:

  • 如果当前行匹配 ( /pattern/! ) 模式 – 分支命令 ( b不带标签) 将输出当前行并加载下一个输入行
  • 否则 – 打印当前行并读取下一行 ( n ),然后应用替换 ( s/../../g ) 并输出结果

3.2. 在下 X 行应用替换

在此示例中,我们的要求是在匹配行的下一行应用替换。因此,我们在解决方案中使用了一次“ n ”命令。

但是,值得一提的是,一旦我们的要求是在匹配行之后的第二行或第三行应用替换,我们可以简单地添加更多的 ’n’ 命令来实现我们的目标

例如,如果我们在第二个sed解决方案中再添加两个*’n’* , sed将在匹配行之后的第三行进行替换:

$ sed '/Quarter [1-4]:/{ n; n; n; s/dollars/$/g }' revenue.txt
Revenue Report
==============
In 2021, we've made in total 4000 dollars' revenue.
Quarter-based revenue report is shown below:
- Quarter 1:
Revenue: 1200 dollars; Profit: 700 dollars
- Quarter 2:
Revenue: 1000 $; Profit: 650 $
- Quarter 3:
Revenue: 1200 dollars; Profit: 800 dollars
- Quarter 4:
Revenue: 600 $; Profit: -200 $
## Total 
Revenue: 4000 dollars
Profit: 1950 dollars

因此,很容易调整命令以适应这种需求变化:添加一定数量的’ n ‘。

但是,我们可能已经意识到,如果“匹配行之后的第x行”这个需求中的数字x比较大,例如8,那么我们必须在命令中放入8个’ n ‘。它使命令更难阅读,而且容易出错。

4. 使用awk命令

awk允许我们使用变量。因此,我们可以灵活地解决问题。

我们先看看 awk的解决方案,然后了解它是如何工作的:

$ awk '/Quarter [1-4]:/{ rl = NR + 1 } NR == rl { gsub( /dollars/,"$") } 1' revenue.txt
Revenue Report
==============
In 2021, we've made in total 4000 dollars' revenue.
Quarter-based revenue report is shown below:
- Quarter 1:
Revenue: 1200 $; Profit: 700 $
- Quarter 2:
Revenue: 1000 $; Profit: 650 $
- Quarter 3:
Revenue: 1200 $; Profit: 800 $
- Quarter 4:
Revenue: 600 $; Profit: -200 $
## Total 
Revenue: 4000 dollars
Profit: 1950 dollars

正如我们在上面的输出中看到的,awk命令完成了这项工作。接下来,让我们快速浏览一下awk one-liner,了解它是如何工作的:

  • 如果当前行与模式匹配 – 将下一行的行号 ( NR + 1 ) 保存在变量 ( rl )中
  • 如果当前行号等于 ’ rl ’ 中的值 - 通过调用**gsub函数应用所需的替换
  • 非零数字 ( 1 ) 将执行默认操作 – 打印当前行

由于我们声明了变量’ rl ‘来保存我们需要执行替换的行号,我们可以修改“ rl = NR + x ”表达式中“ x ”的值以适应不同的要求

例如,命令“ awk ‘/Pattern/{ rl = NR + 8 } NR == rl { gsub( … ) } … ”将在每个匹配行之后的第八行应用替换。从这个角度来看,awk方法比sed解决方案更灵活。