Skip to content

Linux 之 sed 命令

好的,我们来详细讲解一下 Linux 中功能强大的流编辑器——sed 命令。

sed(Stream EDitor)是一个非交互式的、面向行的文本流编辑器。它一次处理一行内容,通过强大的正则表达式匹配,对文本进行查找、替换、删除、插入等操作,是 Shell 脚本和文本处理中不可或缺的工具。

核心概念

  • 非交互式:与 vimnano 不同,你通过命令行预先给出所有编辑指令,sed 会自动完成所有操作,无需人工干预。这使得它非常适合在脚本中使用。
  • 面向流:它从标准输入(如管道 |)或文件中读取文本,处理后将结果输出到标准输出。默认情况下,sed 不会直接修改原文件,这保证了操作的安全性。

基本语法

bash
sed [选项] '指令' 文件名
  • 选项:控制 sed 的行为,如 -i 用于直接修改文件。
  • 指令:告诉 sed 做什么操作,格式通常为 [地址]命令[参数]
  • 文件名:要处理的文件。如果没有文件名或文件名为 -,则从标准输入读取。

常用选项

选项说明
-n静默模式。默认情况下,sed 会输出所有处理过的行。使用 -n 后,只有被 p 命令明确指定的行才会被打印。常与 p 命令一起使用。
-e允许在同一行执行多条指令。例如:sed -e 's/foo/bar/' -e 's/hello/world/' file.txt
-f 脚本文件从指定的脚本文件中读取 sed 指令。
-i[后缀]直接修改文件内容(原地编辑)。如果提供了后缀(如 -i.bak),sed 会先创建一个带此后缀的备份文件,然后再修改原文件。这是一个非常危险但又非常有用的选项,使用前请务必确认指令正确。
-r-E使用扩展正则表达式(ERE),而不是默认的基本正则表达式(BRE)。这样可以使用 +, ?, |,() 等元字符而无需转义,写起来更简洁。

常用指令(命令)

指令是 sed 命令的核心。

1. s (Substitute) 替换命令

这是最常用、最强大的命令。

基本语法: 's/查找模式/替换内容/标志'

  • 查找模式:一个正则表达式。
  • 替换内容:要替换成的文本。
  • 标志
    • g:全局替换。默认只替换每行中第一个匹配项,使用 g 则替换所有匹配项。
    • p:打印。如果替换成功,则打印该行。通常与 -n 选项一起使用。
    • iI:忽略大小写。

示例:

假设我们有文件 test.txt,内容如下:

txt
Hello world
hello world
This is a test line.
test is important.
  • 将每行的第一个 hello 替换为 Hi(忽略大小写)

    bash
    sed 's/hello/Hi/i' test.txt
    # 输出:
    # Hi world
    # Hi world
    # This is a test line.
    # test is important.
  • 全局替换 testEXAMPLE

    bash
    sed 's/test/EXAMPLE/g' test.txt
    # 输出:
    # Hello world
    # hello world
    # This is a EXAMPLE line.
    # EXAMPLE is important.
  • 直接修改文件,并创建备份 test.txt.bak

    bash
    sed -i.bak 's/world/UNIX/g' test.txt

2. d (Delete) 删除命令

删除匹配的行。

示例:

  • 删除包含 test 的行

    bash
    sed '/test/d' test.txt
    # 输出:
    # Hello world
    # hello world
  • 删除第 2 行

    bash
    sed '2d' test.txt
  • 删除第 2 到第 4 行

    bash
    sed '2,4d' test.txt
  • 删除从第 2 行到末尾的所有行

    bash
    sed '2,$d' test.txt

3. p (Print)打印命令

打印匹配的行。通常与 -n 选项一起使用,否则会打印两次(一次是正常输出,一次是 p 命令的输出)。

示例:

  • 只打印包含 hello 的行(类似于 grep

    bash
    sed -n '/hello/p' test.txt
    # 输出:
    # hello world
  • 打印第 1 到第 3 行

    bash
    sed -n '1,3p' test.txt

4. i 插入命令

  • i\text:在指定行之前插入文本。
  • a\text:在指定行之后追加文本。

示例:

在第 2 行之前插入一行 ---INSERT LINE---

bash
sed '2i ---INSERT LINE---' test.txt

5. a 追加命令

在最后一行之后追加 ---APPEND LINE---

bash
sed '$a ---APPEND LINE---' test.txt

6. q 退出指令

q 命令(quit)的作用是:立即退出 sed,不再处理后续的输入行。

q 指令的基本语法是:

shell
[地址]q

1. 基本用法:处理到指定行后退出

假设我们有一个文件 numbers.txt,内容如下:

text
1. First line
2. Second line  
3. Third line
4. Fourth line
5. Fifth line

只处理前3行,然后退出

shell
# 解释:处理完第3行后,sed 立即退出,不再读取第4、5行。
sed '3q' numbers.txt

# 输出
1. First line
2. Second line
3. Third line

2. 处理大文件时提高效率

对于非常大的文件,如果你只需要查看或处理开头部分,使用 q 可以显著提高效率:

shell
# 只查看文件前10行(比 `head -10` 更底层的方法)
sed '10q' large_file.log

# 处理前1000行后退出
sed '1000q' huge_file.csv | some_processing_command

3. 基于模式匹配退出

当找到特定内容时停止处理:

shell
# 遇到空行就退出
sed '/^$/q' file.txt

# 遇到包含 "ERROR" 的行就退出
sed '/ERROR/q' logfile.log

# 遇到以 "exit" 开头的行就退出  
sed '/^exit/q' script.txt

4. 静默模式下的使用

结合 -n 选项,可以在找到目标后静默退出:

shell
# 找到第一个包含 "password" 的行就退出,不输出任何内容
sed -n '/password/{p;q}' config.txt

# 检查文件是否包含某个关键词(通过退出状态判断)
if sed -n '/critical_error/{q1}' app.log; then
    echo "未发现严重错误"
else
    echo "发现严重错误!"
fi

5. 找到第二个匹配项后退出

shell
# 打印直到第二个 "important" 出现
sed -n '/important/{x;/./{x;q};x;p}' file.txt

6. 退出状态码

q 命令可以接受一个可选的退出状态码(GNU sed 扩展):

shell
# 正常退出(状态码 0)
sed '5q' file.txt

# 以状态码 1 退出
sed '/error/q1' file.txt

# 在脚本中用于条件判断
sed '/pattern/{q42}'
echo $? # 如果找到pattern,输出42

7. 在处理后退出

shell
# 替换第一个匹配项后退出
sed 's/old/new/;q' file.txt

# 删除前5行,然后退出
sed '1,5d;6q' file.txt

8. 处理某个范围后退出

shell
# 处理从"START"到"END"标记之间的内容,遇到"END"后退出
sed '/START/,/END/{/END/q;p}' file.txt

9. 快速文件检查

shell
# 检查文件是否超过1000行
if [ $(sed '1001q' file.txt | wc -l) -eq 1000 ]; then
    echo "文件可能超过1000行"
fi

10. 日志文件监控

shell
# 监控日志,发现错误立即报警
tail -f application.log | sed -n '/ERROR/{p;q1}' && send_alert "发现错误!"

11. 配置文件解析

shell
# 只读取配置文件的[Database]部分
sed -n '/^\[Database\]/,/^\[/{/^\[Database\]/n;/^\[/q;p}' config.ini

12. 数据处理管道

shell
# 只处理前100条记录进行分析
cat huge_dataset.csv | sed '100q' | analyze_tool

地址(定址)

地址决定了 sed 命令作用于哪些行。如果省略地址,则命令会作用于每一行

  • 数字:指定行号。如 1 表示第一行。
  • $:最后一行。
  • /正则表达式/:匹配该正则表达式的行。
  • 范围起始地址,结束地址。可以是数字范围(2,5)或正则范围(/foo/,/bar/)。

示例结合地址和命令:

  • 替换第 3 行中的 lineLINE

    bash
    sed '3s/line/LINE/' test.txt
  • 删除从匹配 Hello 的行到匹配 test 的行之间的所有行

    bash
    sed '/Hello/,/test/d' test.txt

高级用法

1. 使用不同的定界符

默认情况下,sed 的替换命令使用斜杠 / 作为定界符。但当查找内容或替换内容中包含斜杠 / 时,就需要频繁转义,命令会变得难以阅读和维护。

sed 允许使用几乎任何字符作为定界符,语法为:

shell
sed 's<定界符>查找内容<定界符>替换内容<定界符>标志'

如果模式或替换内容中包含 /,可以使用其他定界符(如 #, @, |, :, %, _)以避免频繁转义,使命令更清晰。

bash
# 将 /etc/passwd 替换为 /etc/shadow
sed 's/\/etc\/passwd/\/etc\/shadow/' file.txt
# 更清晰的做法:
sed 's#/etc/passwd#/etc/shadow#' file.txt

2. 分组引用 &\1, \2, ...

在替换模式中,& 代表整个匹配的内容。而使用 \(\) 括起来的部分可以在替换字符串中用 \1, \2 等来引用。

  • 给所有单词加上花括号

    bash
    echo "Hello world" | sed 's/[a-zA-Z]*/{&}/g'
    # 输出:{Hello} {world}
  • 调换两个单词的顺序

    bash
    echo "foo bar" | sed 's/\([^ ]*\) \([^ ]*\)/\2 \1/'
    # 输出:bar foo
    # 解释:\([^ ]*\) 匹配第一个非空格字符串(foo),作为分组1;空格后是第二个分组(bar)。替换为 \2 \1 即调换顺序。

总结与最佳实践

  1. 先测试,后执行:在使用 -i 选项直接修改文件前,一定先不加 -i 运行命令,确认输出结果符合预期。
  2. 善用备份:使用 -i.bak 可以在修改前备份原文件,防止误操作。
  3. sed 擅长处理,对于复杂的跨行操作,awkperl 可能是更好的选择。
  4. 结合管道 |sed 可以和其他命令(如 grep, sort, find)完美协作。

sed 的学习曲线稍陡,但一旦掌握,你将拥有一个极其高效的文本处理利器。建议从简单的替换和删除开始练习,逐步深入。