Skip to content

Sed 实用技巧

sed 是一个多功能的工具,通常有多种方法来解决文本处理问题。本节提供了一些“技巧”集合——使用 sed 可以完成的常见任务,通常模拟其他标准 Unix 工具的功能。这些示例展示了 sed 的强大和灵活性。对于某些示例,我们将使用通用的 input.txt 或 data.txt。除非另有说明,否则假设 books.txt 指的是每条目占一行的格式。

方法 1:使用默认行为(不带命令)。

$ sed '' books.txt

方法 2:显式打印每一行。

$ sed -n 'p' books.txt

这两个命令都会显示 books.txt 的全部内容。第二个示例中的 -n 选项抑制了自动打印,因此需要 p (打印) 命令。

从文件中删除所有空行:

$ echo -e "Line #1\n\n\nLine #2" | sed '/^$/d'

输出:

Line #1
Line #2

解释:^$ 是一个匹配空行的正则表达式(行首 ^ 后紧跟着行尾 $)。d 命令从模式空间中删除此类行。

或者,只打印非空行:

$ echo -e "Line #1\n\n\nLine #2" | sed -n '/^$/!p'

输出:

Line #1
Line #2

解释:!p 意味着如果行 不 匹配模式 /^$/,则打印该行。

删除单行注释(例如,C++ 代码中的)

Section titled “删除单行注释(例如,C++ 代码中的)”

考虑一个简单的 C++ 文件 hello.cpp:

#include <iostream>
using namespace std;
int main(void)
{
// Displays message on stdout.
cout << "Hello, World !!!" << endl;
return 0; // Return success.
}

要删除 // 注释:

$ sed 's|//.*||' hello.cpp

输出:

#include <iostream>
using namespace std;
int main(void)
{
cout << "Hello, World !!!" << endl;
return 0;
}

解释:s|//.*|| 将模式 //.*(双斜杠 // 后跟行尾 . 的任意字符 *)替换为空字符串 ||。注意:这是一种简单的方法,可能无法正确处理字符串内部的 // 或复杂代码中的块注释。对于稳健的解析,专用的代码检查器 (linters) 或解析器 (parsers) 更好。

假设 script.sh 包含 shell 命令。要通过在其前添加 # 来注释掉第 3 到第 5 行:

$ sed '3,5s/^/#/' script.sh

转换后的 script.sh 示例输出(未显示原始内容):

#!/bin/bash
pwd
#hostname
#uname -a
#ls
who

解释:3,5 是一个地址范围,选择了第 3 到第 5 行。s/^/#/ 将行首 (^) 替换为 #。

$ sed -n '$=' books.txt

输出(假设 books.txt 有 6 行):

6

解释:$ 定位到最后一行。= 打印其行号。-n 抑制默认的行打印。

要显示 long_file.txt 的前 10 行:

$ sed '10q' long_file.txt

解释:q 命令告诉 sed 在处理第 10 行后退出。对于大文件,这通常比 sed -n '1,10p' 更高效。

$ sed -n '$p' books.txt

输出 (books.txt 的最后一行):

6) A Game of Thrones, George R. R. Martin, 864

解释:$ 定位到最后一行,p 打印它,-n 抑制其他输出。

DOS/Windows 使用回车 + 换行 (\r\n) 作为换行符,而 Unix 使用换行符 (\n)。要进行转换:

$ sed 's/\r$//' dos_file.txt > unix_file.txt

解释:s/\r$// 删除行尾 ($) 的回车符 (\r)。注意:某些 sed 版本可能需要输入 ^M(在许多终端中按 Ctrl+V, Ctrl+M)而不是 \r,如果 \r 未被识别为特殊转义符。

$ sed 's/$/\r/' unix_file.txt > dos_file.txt

解释:s/$/\r/ 将行尾 ($) 替换为回车符 (\r)。sed 在打印时会自动添加换行符 (\n)。

$ sed 's/$/$/' input.txt

解释:s/$/$/ 将行尾(由 $ 匹配)替换为字面字符 $。

$ sed '=' input.txt | sed 'N; s/\n/\t/'

示例输出(对于包含两行 “Line A” 和 “Line B” 的 input.txt)

1 Line A
2 Line B

解释:第一个 sed '=' input.txt 打印行号,然后是换行符,然后是行内容。第二个 sed 命令处理这个输出:N 将下一行(实际内容行)附加到模式空间(它包含行号)。s/\n/\t/ 将行号和内容之间的换行符替换为制表符 (\t)。

$ sed 'w new_copy.txt' original.txt

如果只想写入文件而不打印到标准输出:

$ sed -n 'w new_copy.txt' original.txt

解释:w new_copy.txt 命令将模式空间的内容写入 new_copy.txt,对处理的每一行都执行此操作。

模拟 expand (将制表符转换为空格)

Section titled “模拟 expand (将制表符转换为空格)”

将每个制表符 (tab) 转换为 8 个空格(expand 的常见默认设置):

$ sed 's/\t/ /g' file_with_tabs.txt

解释:s/\t/ /g 将每个制表符 (\t) 替换为八个空格,并对每一行进行全局 (g) 替换。

模拟 tee (输出到标准输出和文件)

Section titled “模拟 tee (输出到标准输出和文件)”
$ sed 'p; w output_file.txt' input.txt

如果默认打印被 -n 抑制(例如,使用了其他命令):

$ sed -n 'p; w output_file.txt' input.txt

解释:p 将当前的模式空间打印到标准输出。w output_file.txt 将模式空间写入 output_file.txt。如果使用不带 -n 的 sed,p 可能会导致重复打印,除非脚本逻辑设计得非常小心。sed 'w output_file.txt' input.txt 也会起作用,它默认打印到标准输出并写入文件。

模拟 cat -s (将多个空行压缩为一个)

Section titled “模拟 cat -s (将多个空行压缩为一个)”

将连续两个或更多空行减少为单个空行:

$ sed '/^$/{N; /\n$/D;}' input_with_blanks.txt

示例:输入 A\n\n\nB 变为 A\n\nB。

解释:这是一种常见的用法。如果一行是空的 (/^$/),N 附加下一行。如果模式空间现在仅包含两个换行符(\n$, 实际上模式是 original_empty_line\nnext_line,所以如果 next_line 也是空的,模式空间看起来就是 \n),D 会删除直到第一个换行符的内容并重新启动循环。这有效地处理了成对的空行。为了完美模拟 cat -s,正确处理所有边缘情况和任意数量的空行,在 POSIX sed 中可能需要更复杂的脚本或多遍处理,或者可以依赖 GNU sed 扩展。

$ sed -n '/pattern/p' input.txt

解释:-n 抑制默认输出。/pattern/p 打印匹配 pattern 的行。

模拟 grep -v (打印不匹配模式的行)

Section titled “模拟 grep -v (打印不匹配模式的行)”
$ sed -n '/pattern/!p' input.txt

解释:/pattern/!p 打印不 (!) 匹配 pattern 的行。

将所有大写字母 ABC 转换为小写字母 abc:

$ echo "ABC TEST" | sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'

输出:

abc test

解释:y/source_chars/dest_chars/ 命令转换字符。source_chars 中的每个字符都会被 dest_chars 中对应位置的字符替换。