介绍

sed是一款优秀的文本处理程序,但是其不同版本在用法和参数上存在着较大差异,建议大家在使用时一定要查询相关文档,以免出错。

以下示例大部分都是针对BSD版本的sed,因为OSX系统自带的sed就是这个版本,我是在mac上完成的相关测试。

一般linux的发布版带的都是GNU版本的,个人感觉,GNU版本的更强大和方便。

sed和perl正则表达式一些元字符的比较

+---------------------+
|perl   |sed          |
|-------+-------------|
|\d     |[[:digit:]]  |
|-------+-------------|
|\s     |\s           |
|-------+-------------|
|+      |\+           |
|-------+-------------|
|{num}  |\{num\}      |
+---------------------+

常用命令

如果希望直接在源文件中修改,传参数-i[.bak],在GNU sed中,.bak可选,但是在BSD sed中,必须添加,可以传递空字符串来表示直接修改源文件,即使用-i ''

用于测试的pets.txt下载

# 删除只有空白字符行
sed -i -e '/^\s\+$/d' file #GNU

sed '1!G;h;$!d' pets.txt #反转一个文件的行
sed 'N; s/\n  /, /' pets.txt #将两行合并,并用逗号分开
sed -e 's/'$(echo -e "\x15")'//g' file # sed用于去除ascii不可打印的控制字符

# 常规的替换功能
sed "s/my/Hao Chen's/" pets.txt
sed "3s/my/your/" pets.txt
sed "3,6s/my/your/" pets.txt #只替换第3到第6行的文本

sed -n '/cat/,/fish/p' pets.txt # 只打印匹配cat和fish之前的行,-n表示不输出那些未匹配的行

sed -e 3,6{ -e /This/d -e } pets.txt
sed '3,6{/This/d;}' pets.txt #BSD sed, must add semi-colon

sed '3,6 {/This/{/fish/d;};}' pets.txt
sed '1,${/This/d;s/^ *//g;}' pets.txt
sed -E '/dog/{N;N;N;s/(^|\n)/&# /g;}' pets.txt #BSD sed
sed '/dog/,+3s/^/# /g' pets.txt #GNU sed
sed = pets.txt | sed 'N;s/\n/'$'\t''/' > line_num_pets.txt
sed = my.txt | sed 'N; s/^/    /; s/\(.\{5,\}\)\n/\1 /' #对文件中的所有行编号(行号在左,文字左端对齐)。
#sed = my.txt | sed -E 'N; s/(.*)\n/    \1 /' #貌似,我也可以这么写
sed '/'"$name"'/,/};/d' back_slash.txt
#实际应用,用来做代码重构时:(err) -> next err  =>   next
find . -name '*.coffee' | xargs sed -i '' -E '/\(err\) ->/{N;s/\(err\) ->\n +next err/next/g;}'
#在多行的时候,[[:space:]]才会匹配换行符\n,只有单行的时候,字符串尾部的换行符已经被sed截掉,所以匹配不到

#使用GNU sed去掉首行的BOM
find . -name '*.srt' -exec gsed -i -e '1s/^\xEF\xBB\xBF//' {} \;

gsed '1~2G' -i *.txt #奇数行后加空行
gsed -i -e '$a\' file #如果文件末尾没有换行符,则自动添加,解决"No newline at end of file"的问题,OSX sed中:sed -i '' -e '$a\'

除了在命令行中使用字符串输入,也可以使用处理脚本作为输入,脚本3_6下载

sed -f 3_6 pets.txt #use file

神奇用法

# BSD sed中,插入一段文本的做法
sed -iold '1i\'$'\n''text to prepend'$'\n' file.txt
# $'\n'是bash中的换行符,具体执行原理如下:
         sed -iold '1i\'$'\n''text to prepend'$'\n' file.txt
                      ^^^^^^^^                     ^
                     / |\|||/ \                    |__ No need to reopen
                     | | \|/  |                          string to sed
     Tells sed to    | |  |   |
    escape the next _/ |  |   +-----------------------------+
         char          |  +-------------+                   |
                       |                |                   |
                  Close string   The  special bash   Reopen string to
                     to sed       newline char to      send to sed
                                    send to sed

下面为应用以上原理的示例。这些示例都是针对BSD sed的。当替换文本中有单引号时,可以使用双引号圈引,减少转义。这是用于测试的my.txt下载

# 在第1行插入内容
sed "1i\\"$'\n'"my monkey's name is wukong"$'\n' my.txt

# 在最后一行追加内容
sed "$ a \\"$'\n'"my monkey's name is wukong"$'\n' my.txt

# 在匹配行之前插入内容
sed "/fish/i\\"$'\n'"my monkey's name is wukong"$'\n' my.txt

# 在指定行修改为特定内容
sed "2c \\"$'\n'"my monkey's name is wukong"$'\n' my.txt
# 将my.txt中,每行的s,从第2个开始,全部替换为xxx
sed 's/s/\'$'\n''xxx/2;h;s/s/xxx/g;H;g;s/\n.*\n//' my.txt

单行命令总结

# 匹配行后追加内容
gsed -i '/matchpattern/a content' tmp.txt
# 如果content是以空白字符开头,比如tab或者空格,则用一个反斜线标记出来
gsed -i '/matchpattern/a \ content' tmp.txt
# 命令行中输入tab键:先按ctrl+v,然后再按tab
gsed -i '/matchpattern/a \	content' tmp.txt
# 匹配行后追加多行内容,可以把需要追加的内容写到一个文件中,然后使用r命令
gsed -i '/abcd/r otherfile.txt' tmp.txt

# 删除匹配行之前的内容,也就是保留匹配行之后的所有内容
sed -i '/^package /,$!d' *.go
# 显示一定范围内的行
sed -n '190,200p' tmp.txt