sed举例

一直没有系统学习过sed,这里是一些例子的汇总整理,快刀斩乱麻......

样例文本file内容如下

Cygwin
Unix
Linux
Solaris
AIX
  • 删除文件中指定行

例如删除第1行

sed '1d' file

删除第3行

sed '3d' file

上述sed命令是直接将sed修改后的内容出处到标准输出(屏幕)上,而源文件不变。要更新源文件,需要加上-i参数。例如:

sed -i '3d' file

sed是将源文件复制后修改,所以-i参数实际是修改后覆盖源文件

sed 中, $ 代表最后一行,所以删除最后一行可以使用如下命令:

sed '$d' file

此外, head 命令提供了一个更好的删除最后几行的方法, -n -X :通常 -n X 表示打印前X行,当 X 前面加上 - 则表示倒过来,即打印所有行但不包括最后的X行,也就是输出内容少了最后 X 行,所以定向到新文件就能截断原文件的最后 X 行(以下案例去除了file.txt文件的最后3行生成new_file.txt):

cat file.txt | head -n -3 > new_file.txt

删除第2行到第四行

sed '2,4d' file

保留第2行到第4行,删除其余行(反转选择就是加上!符号)

sed '2,4!d' file

这里输出就是

Unix
Linux
Solaris

删除第1行和最后一行(注意:每一行之间用;分隔)

sed '1d;$d' file

同上,如果要删除第2行和第4行,就使用'2d;3d'

  • 匹配并删除

删除带有一个特定字符开头的行,例如,L开头的行

sed '/^L/d' file

这里输出就是

Cygwin
Unix
Solaris
AIX

删除x结尾的行

sed '/x$/d' file

还支持正则,例如,要删除xX结尾的行

sed '/[xX]$/d' file

删除文件中的空白行

sed '/^$/d' file

^表示开头,$表示结尾,由于中间没有任何字符,就是空白行

删除空白行或者包含一些空格的行

sed '/^ *$/d' file

这里*表示0个或者多个重复的前一个字符,这里前一个字符是空格,所以这里 *就表示有一个空格或者多个空格。此时会删除完全空白行,以及包含一个或多个空格的空白行。

删除全部是大写字符的行(这里会删除AIX行)

sed '/^[A-Z]*$/d' file

[A-Z]表示全部大写字母,后面跟了*表示前面的[A-Z]字符重复(任意大写字符),也就是表示整行都是大写字符,所以就删除了AIX

删除没有包含字符串Unix的行

sed '/Unix/!d' file

删除包含UnixLinux的行(或的符号是|,注意需要加转义符)

sed '/Unix\|Linux/d' file

删除从第一行到匹配上Linux的行

sed '1,/Linux/d' file

输出的结果是

Solaris
AIX

删除从匹配Linux行到文档最后行

sed '/Linux/,$d' file

如果最后一行包含了AIX,则删除最后一行(如果最后一行是其他内容则不删除)

sed '${/AIX/d;}' file

输出内容是

Cygwin
Unix
Linux
Solaris

这里$表示最后一行,只在这行包含了AIX才删除,这里引入了if判断(也就是{}表示if条件)

如果最后一行包含了AIX或者HPUX则删除最后一行

sed '${/AIX\|HPUX/d;} file

只在第1行到第4行之间出现Solaris字符串匹配情况下删除这行:

sed '1,4{/Solaris/d;}' file

如果一行中包含了Unix关键字,则删除这行和下面一行

sed '/Unix/{N;d;}' file

这里N命令读取匹配行的下一行,而d命令则删除匹配行和下一行

上述命令输出是

Cygwin
Solaris
AIX

只删除包含了Unix的下一行(但Unix行不删除)

sed '/Unix/{N;s/\n.*//;}' file

输出内容是

Cygwin
Unix
Solaris
AIX

sed - 25 examples to delete a line or pattern in a file原文还有3个例子较为复杂:

删除匹配行和匹配行之前的行

sed -n '/Linux/{s/.*//;x;d;};x;p;${x;p;}' file | sed '/^$/d'

删除包含Linux的行之前的行,但不删除'Linux'行:

sed -n '/Linux/{x;d;};1h;1!{x;p;};${x;p;}' file

删除包含Linux行以及之前的行和之后好的行

sed -n '/Linux/{N;s/.*//;x;d;};x;p;${x;p;}' file | sed '/^$/d'
  • 合并重复的空格成一个空格

在使用cut工具来截取ps出来的进程的pid,会遇到一个问题,就是每列之间的空格数量是不一定的,这样虽然可以通过awk来截取,但是蹪于cut命令就不行了。解决的方法是将多个空格合并成一个空格,sed命令提供了这个功能:

ps axu | grep '[j]boss' | sed 's/\s\+/ /g' | cut -d' ' -f2

这里使用的是GNU sed,这个sed提供了\s表示空格(扩展),\+表示多个空格

或者

ps axu | grep '[j]boss' | sed 's/\s\s*/ /g' | cut -d' ' -f2

不过,在OS X中,需要传递-E参数来激活sed扩展正则表达式,然后使用[[:space:]]来代替\s,也就是

ps axu | grep '[j]boss' | sed -E 's/[[:space:]]+/ /g' | cut -d' ' -f2

请参考cut使用举例

  • 替换换行符(): 读取整个文件替换换行符成一个空格

sed ':a;N;$!ba;s/\n/ /g'

首先创建一个label :a

通过N将当前行和下一行通过空格连接

如果到了最后一行,则创建label $!ba (这里$!表示不在最后一行做这个操作)

最后使用在模式坑拜拜中的空格来替换每个换行符

上述方法实在太拗口,其实可以使用tr工具来实现这个任务

tr '\n' ' ' < input_filename

或者

tr --delete '\n' < input.txt > output.txt
  • 在匹配行的最后添加内容

sed -i '/kernel \/boot/s/$/ clocksource_failover=acpi_pm/' /boot/grub/grub.cfg

sed -i '/vmlinuz-2.6.32/ s/$/ intremap=off/' /boot/grub/grub.conf

上述方法简单来说就是先匹配,然后搜索到最后的标志$,再进行替换。

参考

How to append a string at end of a specific line in a file in bash

Add to the end of a line containing a pattern - with sed or awk

  • 将换行符替换成字符

例如,我有一个文件包含了多个主机名(每行是一个主机名),但是提交到某个平台的时候,格式要求使用,分隔,则可以通过sed来替换换行符

sed ':a;N;$!ba;s/\n/,/g' file

含义如下:

  • 创建一个标签(liabel) :a

  • 通过添加N到当前和下一行的位置

  • 如果在最后一行之前,则创建标签$!ba$!表示如果最后一行就不做)

  • 最后将所有换行符替换成,

上述命令是使用GNU sed,如果是跨平台,则使用BSD sed

sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/,/g'

参考 How can I replace a newline (\n) using sed?

更为简单的方法是使用tr,不过结尾多一个,

tr '\n' ',' < input_filename

怎样去除最后一个字符,应该可以使用sedawk,不过 Delete the last character of a string using string manipulation in shell script 提供了一个巧妙的方法,组合利用rev命令和cut命令。即先将字符串倒转(rev),然后用cut -c 2-截取出(n+1)-之后所有字符,然后再倒转回来

cat input_filename | tr '\n' ',' | rev | cut -c 2- | rev

还有一种方法是使用bash 4.2之后,支持字符串变量切片

a=`cat input_filename | tr '\n' ','`
echo "${a::-1}"

匹配行修改

例如我需要修改 /etc/fstab ,将包含 swap 行注释掉。参考 sed - Commenting a line matching a specific string AND that is not already commented out

原先的 /etc/fstab 内容如下:

...
UUID=57e88efb-83f7-42a3-8080-f943626bd7f7 swap                    swap    defaults        0 0

修改命令如下:

sed -i -e '/swap/s/^UUID=/#UUID=/g' /etc/fstab

上述表示先匹配swap行,然后搜索 UUID= 开头的内容替换成 #UUID=

占位符替换思路

当需要向海量服务器部署配置脚本,但是配置脚本中某些字符串和服务器主机名相关(需要改成主机名),则可以模仿puppet之类的配置管理工具,采用配置文件中占位符方式来实现。

即预先分发的配置文件中包含一些特定的预先占好位置的变量字符串(具有特征,如全大写,或者有特殊符号),在分发完成后,采用pssh命令对配置文件中占位符变量进行替换。

举例:配置文件 /etc/example.conf 内容如下:

...
console name='HOSTNAME' dev='/dev/null' opts='-o example'

HOSTNAME就是需要替换的占位符变量

通过pscp分发完配置文件到所有服务器之后,就可以通过以下pssh命令将每个主机的主机名提取出来,然后用实际主机名替换HOSTNAME

pssh -ih server_list "HOST=\$(hostname);sudo sed -i \"s/HOSTNAME/\$HOST/\" /etc/example.conf"

sed多个匹配

sed支持一次检查多个匹配,实际上就是sed多个指令的意思。注意:指令之间使用;分隔:

sed -i '/PATTERN_1/d;PATTERN_2/d;PATTERN_3/d;/^$/d' myfile.txt

sed转换文档实现unix2dos

  • unix2dos

sed 's/$'"/`echo \\\r`/" disk.txt > disk_dos.txt
  • dos2unix

sed 's/^M$//' disk_dos.txt > disk.txt

参考 HowTo: UNIX / Linux Convert DOS Newlines CR-LF to Unix/Linux Format

参考

Last updated