Linux 之 sed 命令
好的,我们来详细讲解一下 Linux 中功能强大的流编辑器——sed 命令。
sed(Stream EDitor)是一个非交互式的、面向行的文本流编辑器。它一次处理一行内容,通过强大的正则表达式匹配,对文本进行查找、替换、删除、插入等操作,是 Shell 脚本和文本处理中不可或缺的工具。
核心概念
- 非交互式:与
vim或nano不同,你通过命令行预先给出所有编辑指令,sed会自动完成所有操作,无需人工干预。这使得它非常适合在脚本中使用。 - 面向流:它从标准输入(如管道
|)或文件中读取文本,处理后将结果输出到标准输出。默认情况下,sed不会直接修改原文件,这保证了操作的安全性。
基本语法
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选项一起使用。i或I:忽略大小写。
示例:
假设我们有文件 test.txt,内容如下:
Hello world
hello world
This is a test line.
test is important.将每行的第一个
hello替换为Hi(忽略大小写):bashsed 's/hello/Hi/i' test.txt # 输出: # Hi world # Hi world # This is a test line. # test is important.全局替换
test为EXAMPLE:bashsed 's/test/EXAMPLE/g' test.txt # 输出: # Hello world # hello world # This is a EXAMPLE line. # EXAMPLE is important.直接修改文件,并创建备份
test.txt.bak:bashsed -i.bak 's/world/UNIX/g' test.txt
2. d (Delete) 删除命令
删除匹配的行。
示例:
删除包含
test的行:bashsed '/test/d' test.txt # 输出: # Hello world # hello world删除第 2 行:
bashsed '2d' test.txt删除第 2 到第 4 行:
bashsed '2,4d' test.txt删除从第 2 行到末尾的所有行:
bashsed '2,$d' test.txt
3. p (Print)打印命令
打印匹配的行。通常与 -n 选项一起使用,否则会打印两次(一次是正常输出,一次是 p 命令的输出)。
示例:
只打印包含
hello的行(类似于grep):bashsed -n '/hello/p' test.txt # 输出: # hello world打印第 1 到第 3 行:
bashsed -n '1,3p' test.txt
4. i 插入命令
i\text:在指定行之前插入文本。a\text:在指定行之后追加文本。
示例:
在第 2 行之前插入一行 ---INSERT LINE---:
sed '2i ---INSERT LINE---' test.txt5. a 追加命令
在最后一行之后追加 ---APPEND LINE---:
sed '$a ---APPEND LINE---' test.txt6. q 退出指令
q 命令(quit)的作用是:立即退出 sed,不再处理后续的输入行。
q 指令的基本语法是:
[地址]q1. 基本用法:处理到指定行后退出
假设我们有一个文件 numbers.txt,内容如下:
1. First line
2. Second line
3. Third line
4. Fourth line
5. Fifth line只处理前3行,然后退出
# 解释:处理完第3行后,sed 立即退出,不再读取第4、5行。
sed '3q' numbers.txt
# 输出
1. First line
2. Second line
3. Third line2. 处理大文件时提高效率
对于非常大的文件,如果你只需要查看或处理开头部分,使用 q 可以显著提高效率:
# 只查看文件前10行(比 `head -10` 更底层的方法)
sed '10q' large_file.log
# 处理前1000行后退出
sed '1000q' huge_file.csv | some_processing_command3. 基于模式匹配退出
当找到特定内容时停止处理:
# 遇到空行就退出
sed '/^$/q' file.txt
# 遇到包含 "ERROR" 的行就退出
sed '/ERROR/q' logfile.log
# 遇到以 "exit" 开头的行就退出
sed '/^exit/q' script.txt4. 静默模式下的使用
结合 -n 选项,可以在找到目标后静默退出:
# 找到第一个包含 "password" 的行就退出,不输出任何内容
sed -n '/password/{p;q}' config.txt
# 检查文件是否包含某个关键词(通过退出状态判断)
if sed -n '/critical_error/{q1}' app.log; then
echo "未发现严重错误"
else
echo "发现严重错误!"
fi5. 找到第二个匹配项后退出
# 打印直到第二个 "important" 出现
sed -n '/important/{x;/./{x;q};x;p}' file.txt6. 退出状态码
q 命令可以接受一个可选的退出状态码(GNU sed 扩展):
# 正常退出(状态码 0)
sed '5q' file.txt
# 以状态码 1 退出
sed '/error/q1' file.txt
# 在脚本中用于条件判断
sed '/pattern/{q42}'
echo $? # 如果找到pattern,输出427. 在处理后退出
# 替换第一个匹配项后退出
sed 's/old/new/;q' file.txt
# 删除前5行,然后退出
sed '1,5d;6q' file.txt8. 处理某个范围后退出
# 处理从"START"到"END"标记之间的内容,遇到"END"后退出
sed '/START/,/END/{/END/q;p}' file.txt9. 快速文件检查
# 检查文件是否超过1000行
if [ $(sed '1001q' file.txt | wc -l) -eq 1000 ]; then
echo "文件可能超过1000行"
fi10. 日志文件监控
# 监控日志,发现错误立即报警
tail -f application.log | sed -n '/ERROR/{p;q1}' && send_alert "发现错误!"11. 配置文件解析
# 只读取配置文件的[Database]部分
sed -n '/^\[Database\]/,/^\[/{/^\[Database\]/n;/^\[/q;p}' config.ini12. 数据处理管道
# 只处理前100条记录进行分析
cat huge_dataset.csv | sed '100q' | analyze_tool地址(定址)
地址决定了 sed 命令作用于哪些行。如果省略地址,则命令会作用于每一行。
- 数字:指定行号。如
1表示第一行。 $:最后一行。/正则表达式/:匹配该正则表达式的行。- 范围:
起始地址,结束地址。可以是数字范围(2,5)或正则范围(/foo/,/bar/)。
示例结合地址和命令:
替换第 3 行中的
line为LINE:bashsed '3s/line/LINE/' test.txt删除从匹配
Hello的行到匹配test的行之间的所有行:bashsed '/Hello/,/test/d' test.txt
高级用法
1. 使用不同的定界符
默认情况下,sed 的替换命令使用斜杠 / 作为定界符。但当查找内容或替换内容中包含斜杠 / 时,就需要频繁转义,命令会变得难以阅读和维护。
sed 允许使用几乎任何字符作为定界符,语法为:
sed 's<定界符>查找内容<定界符>替换内容<定界符>标志'如果模式或替换内容中包含 /,可以使用其他定界符(如 #, @, |, :, %, _)以避免频繁转义,使命令更清晰。
# 将 /etc/passwd 替换为 /etc/shadow
sed 's/\/etc\/passwd/\/etc\/shadow/' file.txt
# 更清晰的做法:
sed 's#/etc/passwd#/etc/shadow#' file.txt2. 分组引用 & 和 \1, \2, ...
在替换模式中,& 代表整个匹配的内容。而使用 \( 和 \) 括起来的部分可以在替换字符串中用 \1, \2 等来引用。
给所有单词加上花括号:
bashecho "Hello world" | sed 's/[a-zA-Z]*/{&}/g' # 输出:{Hello} {world}调换两个单词的顺序:
bashecho "foo bar" | sed 's/\([^ ]*\) \([^ ]*\)/\2 \1/' # 输出:bar foo # 解释:\([^ ]*\) 匹配第一个非空格字符串(foo),作为分组1;空格后是第二个分组(bar)。替换为 \2 \1 即调换顺序。
总结与最佳实践
- 先测试,后执行:在使用
-i选项直接修改文件前,一定先不加-i运行命令,确认输出结果符合预期。 - 善用备份:使用
-i.bak可以在修改前备份原文件,防止误操作。 sed擅长处理行,对于复杂的跨行操作,awk或perl可能是更好的选择。- 结合管道
|,sed可以和其他命令(如grep,sort,find)完美协作。
sed 的学习曲线稍陡,但一旦掌握,你将拥有一个极其高效的文本处理利器。建议从简单的替换和删除开始练习,逐步深入。
