Stream 编辑器 - 字符串

替换命令

诸如"查找和替换"之类的文本替换操作在任何文本编辑器中都很常见。 在本节中,我们将说明 SED 如何执行文本替换。 下面给出了替换命令的语法。

[address1[,address2]]s/pattern/replacement/[flags]

这里,address1address2 分别是起始地址和结束地址,可以是行号或模式字符串。 这两个地址都是可选参数。 模式是我们想要用替换字符串替换的文本。 此外,我们可以使用 SED 指定可选标志。

在books.txt 文件中,我们使用逗号(,) 分隔每一列。 让我们使用竖线(|)来分隔每一列。 为此,请将逗号 (,) 替换为竖线 (|)。

[jerry]$ sed 's/,/ | /' books.txt

执行上述代码,得到如下结果:

1) A Storm of Swords | George R. R. Martin, 1216 
2) The Two Towers | J. R. R. Tolkien, 352 
3) The Alchemist | Paulo Coelho, 197 
4) The Fellowship of the Ring | J. R. R. Tolkien, 432 
5) The Pilgrimage | Paulo Coelho, 288 
6) A Game of Thrones | George R. R. Martin, 864 

如果你仔细观察,只有第一个逗号被替换,第二个保持原样。 为什么? 一旦模式匹配,SED 就会用替换字符串替换它并移动到下一行。 默认情况下,它只替换第一次出现。 要替换所有匹配项,请使用带有 SED 的全局标志 (g),如下所示:

[jerry]$ sed 's/,/ | /g' books.txt

执行上述代码,得到如下结果:

1) A Storm of Swords | George R. R. Martin | 1216 
2) The Two Towers | J. R. R. Tolkien | 352 
3) The Alchemist | Paulo Coelho | 197 
4) The Fellowship of the Ring | J. R. R. Tolkien | 432 
5) The Pilgrimage | Paulo Coelho | 288 
6) A Game of Thrones | George R. R. Martin | 864

现在所有出现的逗号 (,) 都替换为竖线 (|)。

我们可以指示 SED 仅在模式匹配成功时执行文本替换。 以下示例仅当一行包含模式 The Pilgrimage 时才将逗号 (,) 替换为竖线 (|)。

[jerry]$ sed '/The Pilgrimage/ s/,/ | /g' books.txt 

执行上述代码,得到如下结果:

1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage | Paulo Coelho | 288 
6) A Game of Thrones, George R. R. Martin, 864

除此之外,SED 可以替换特定出现的模式。 让我们用竖线(|) 仅替换逗号(,) 的第二个实例。

[jerry]$ sed 's/,/ | /2' books.txt

执行上述代码,得到如下结果:

1) A Storm of Swords, George R. R. Martin | 1216 
2) The Two Towers, J. R. R. Tolkien | 352 
3) The Alchemist, Paulo Coelho | 197 
4) The Fellowship of the Ring, J. R. R. Tolkien | 432 
5) The Pilgrimage,Paulo Coelho | 288 
6) A Game of Thrones, George R. R. Martin  | 864

在上面的示例中,SED 命令末尾(或标志位置)的数字表示第 2 次出现。

SED 提供了一个有趣的功能。 执行替换后,SED 提供了一个选项来仅显示更改的行。 为此,SED 使用 p 标志,它指代打印。 以下示例仅列出更改的行。

[jerry]$ sed -n 's/Paulo Coelho/PAULO COELHO/p' books.txt

执行上述代码,得到如下结果:

3) The Alchemist, PAULO COELHO, 197 
5) The Pilgrimage, PAULO COELHO, 288 

我们也可以将更改的行存储在另一个文件中。 要实现此结果,请使用 w 标志。 以下示例显示了如何执行此操作。

[jerry]$ sed -n 's/Paulo Coelho/PAULO COELHO/w junk.txt' books.txt

我们使用了相同的 SED 命令。 让我们验证 junk.txt 文件的内容。

[jerry]$ cat junk.txt

执行上述代码,得到如下结果:

3) The Alchemist, PAULO COELHO, 197 
5) The Pilgrimage, PAULO COELHO, 288

要执行不区分大小写的替换,请使用表示忽略大小写的 i 标志。 以下示例执行不区分大小写的替换。

[jerry]$ sed  -n 's/pAuLo CoElHo/PAULO COELHO/pi' books.txt

执行上述代码,得到如下结果:

3) The Alchemist, PAULO COELHO, 197 
5) The Pilgrimage, PAULO COELHO, 288

到目前为止,我们只使用了前斜杠(/)字符作为分隔符,但我们也可以使用竖线(|)、at 符号(@)、插入符号(^)、感叹号(!)作为分隔符。 以下示例显示如何使用其他字符作为分隔符。

假设您需要将路径 /bin/sed 替换为 /home/jerry/src/sed/sed-4.2.2/sed。 因此,您的 SED 命令 看起来像这样:

[jerry]$ echo "/bin/sed" | sed 's/\/bin\/sed/\/home\/jerry\/src\/sed\/sed-4.2.2\/sed/'

执行上述代码,得到如下结果:

/home/jerry/src/sed/sed-4.2.2/sed

我们可以使这个命令更具可读性和易于理解。 让我们使用竖线 (|) 作为分隔符并查看结果。

[jerry]$ echo "/bin/sed" | sed 's|/bin/sed|/home/jerry/src/sed/sed-4.2.2/sed|'

执行上述代码,得到如下结果:

/home/jerry/src/sed/sed-4.2.2/sed

的确! 我们得到了相同的结果,并且语法更具可读性。 同样,我们可以使用"at"符号 (@) 作为分隔符,如下所示:

[jerry]$ echo "/bin/sed" | sed 's@/bin/sed@/home/jerry/src/sed/sed-4.2.2/sed@'

执行上述代码,得到如下结果:

/home/jerry/src/sed/sed-4.2.2/sed 

除此之外,我们还可以使用插入符号(^) 作为分隔符。

[jerry]$ echo "/bin/sed" | sed 's^/bin/sed^/home/jerry/src/sed/sed-4.2.2/sed^'

执行上述代码,得到如下结果:

/home/jerry/src/sed/sed-4.2.2/sed 

我们还可以使用感叹号 (!) 作为分隔符,如下所示:

[jerry]$ echo "/bin/sed" | sed 's!/bin/sed!/home/jerry/src/sed/sed-4.2.2/sed!'

执行上述代码,得到如下结果:

/home/jerry/src/sed/sed-4.2.2/sed 

通常,反斜杠 (/) 用作分隔符,但有时将其他支持的分隔符与 SED 一起使用会更方便。


创建子字符串

我们学习了强大的替代命令。 让我们看看是否可以从匹配的文本中找到子字符串。 让我们通过一个例子来了解如何做到这一点。

Let us consider the following text:

[jerry]$ echo "Three One Two"

假设我们必须将它排列成一个序列。 意思是,它应该先打印一个,然后是二,最后是三。 下面的单线可以满足需要。

echo "Three One Two" | sed 's|\(\w\+\) \(\w\+\) \(\w\+\)|\2 \3 \1|'

请注意,在上面的示例中,竖线 (|) 用作分隔符。

在 SED 中,可以使用分组运算符指定子字符串,并且必须以转义字符作为前缀,即 \(\)

\w 是匹配任何字母、数字或下划线的正则表达式,"+"用于匹配多个字符。 换句话说,正则表达式 \(\w\+\) 匹配输入字符串中的单个单词。

在输入字符串中,三个单词用空格隔开,因此有三个正则表达式用空格隔开。 第一个正则表达式存储第一个单词,即Three,第二个存储单词One,第三个存储单词Two

这些子字符串由 \N, 引用,其中 N 是子字符串编号。因此,\2 打印第二个子字符串,即 One; \3 打印第三个子字符串,即 Two;\1 打印第一个子字符串,即 Three

让我们用逗号 (,) 分隔这些单词并相应地修改正则表达式。

[jerry]$ echo "Three,One,Two" | sed 's|\(\w\+\),\(\w\+\),\(\w\+\)|\2,\3,\1|'

执行上述代码,得到如下结果:

One,Two,Three

请注意,现在正则表达式中有逗号(,)而不是空格。


字符串替换标志(仅限 GNU SED)

在上一节中,我们看到了替换命令的一些示例。 GNU SED 提供了一些特殊的转义序列,可以在替换字符串中使用。 请注意,这些字符串替换标志是 GNU 特定的,可能不适用于 SED 的其他变体。 在这里,我们将讨论字符串替换标志。

  • \L: 当替换字符串中指定了 \L 时,它会将 \L 之后单词的所有剩余字符视为小写字符。 例如,字符"ULO"被视为小写字符。

[jerry]$ sed -n 's/Paulo/PA\LULO/p' books.txt

在执行上述代码时,您会得到以下结果:

3) The Alchemist, PAulo Coelho, 197
5) The Pilgrimage, PAulo Coelho, 288
  • \u: 在替换字符串中指定 \u 时,它将 \u 之后的立即字符视为大写字符。 在以下示例中,\u 用于字符"a"和"o"之前。 因此 SED 将这些字符视为大写字母。

[jerry]$ sed -n 's/Paulo/p\uaul\uo/p' books.txt

执行上述代码,得到如下结果:

3) The Alchemist, pAulO Coelho, 197 
5) The Pilgrimage, pAulO Coelho, 288
  • \U: 当替换字符串中指定了 \U 时,它会将 \U 之后单词的所有剩余字符视为大写字符。

[jerry]$ sed -n 's/Paulo/\Upaulo/p' books.txt 

执行上述代码,得到如下结果:

3) The Alchemist, PAULO Coelho, 197 
5) The Pilgrimage, PAULO Coelho, 288
  • \E: 此标志应与 \L 或 \U 一起使用。 它停止由标志 \L 或 \U 启动的转换。 在下面的示例中,只有第一个单词被替换为大写字母。

[jerry]$ sed -n 's/Paulo Coelho/\Upaulo \Ecoelho/p' books.txt

执行上述代码,得到如下结果:

3) The Alchemist, PAULO coelho, 197 
5) The Pilgrimage, PAULO coelho, 288