Sed 介绍
sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行替换、删除、新增、选取等特定工作,下面先了解一下sed的用法sed命令行格式为:
sed [-nefri] ‘command’ 输入文本
常用选项:
-n∶使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN的资料一般都会被列出到萤幕上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e∶直接在指令列模式上进行 sed 的动作编辑;
-f∶直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的sed 动作;
-r∶sed 的动作支援的是延伸型正规表示法的语法。(预设是基础正规表示法语法)
-i∶直接修改读取的档案内容,而不是由萤幕输出。
常用命令:
a ∶新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
c ∶取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d ∶删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
i ∶插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p ∶列印,亦即将某个选择的资料印出。通常 p 会与参数 sed -n 一起运作~
s ∶取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦!
举例:(假设我们有一文件名为ab)
删除某行
[root@localhost ruby] # sed '1d' ab #删除第一行
[root@localhost ruby] # sed '$d' ab #删除最后一行
[root@localhost ruby] # sed '1,2d' ab #删除第一行到第二行
[root@localhost ruby] # sed '2,$d' ab #删除第二行到最后一行
显示某行
. [root@localhost ruby] # sed -n '1p' ab #显示第一行
[root@localhost ruby] # sed -n '$p' ab #显示最后一行
[root@localhost ruby] # sed -n '1,2p' ab #显示第一行到第二行
[root@localhost ruby] # sed -n '2,$p' ab #显示第二行到最后一行
使用模式进行查询
[root@localhost ruby] # sed -n '/ruby/p' ab #查询包括关键字ruby所在所有行
[root@localhost ruby] # sed -n '/\$/p' ab #查询包括关键字$所在所有行,使用反斜线\屏蔽特殊含义
增加一行或多行字符串
[root@localhost ruby]# cat ab
Hello!
ruby is me,welcome to my blog.
end
[root@localhost ruby] # sed '1a drink tea' ab #第一行后增加字符串"drink tea"
Hello!
drink tea
ruby is me,welcome to my blog.
end
[root@localhost ruby] # sed '1,3a drink tea' ab #第一行到第三行后增加字符串"drink tea"
Hello!
drink tea
ruby is me,welcome to my blog.
drink tea
end
drink tea
[root@localhost ruby] # sed '1a drink tea\nor coffee' ab #第一行后增加多行,使用换行符\n
Hello!
drink tea
or coffee
ruby is me,welcome to my blog.
end
代替一行或多行
[root@localhost ruby] # sed '1c Hi' ab #第一行代替为Hi
Hi
ruby is me,welcome to my blog.
end
[root@localhost ruby] # sed '1,2c Hi' ab #第一行到第二行代替为Hi
Hi
end
替换一行中的某部分
格式:sed 's/要替换的字符串/新的字符串/g' (要替换的字符串可以用正则表达式)
[root@localhost ruby] # sed -n '/ruby/p' ab | sed 's/ruby/bird/g' #替换ruby为bird
[root@localhost ruby] # sed -n '/ruby/p' ab | sed 's/ruby//g' #删除ruby
插入
[root@localhost ruby] # sed -i '$a bye' ab #在文件ab中最后一行直接输入"bye"
[root@localhost ruby]# cat ab
Hello!
ruby is me,welcome to my blog.
end
bye
$ sed 's/test/mytest/g' example-----在整行范围内把test替换为mytest。如果没有g标记,则只有每行第一个匹配的test被替换成mytest。
//===============================
Sed 介绍
关于Sed的可怕事实
Sed是一个终极的流编辑器。如果你感觉流编辑器听起来很奇怪,就想象一下一个通过管道的水流。是的,如果这个水流参管道里,你不可能看到水流。这就是我拿水流比喻的原因。
不管怎么说,sed是一个非常有用的工具,不幸的是,大多数人没见识过它的真正的力量。这个语言非常简单,但文档糟糕透了。Solais上关于sed的在线文档有5页长,其中2页描述了34种你可能会看到的错误,一个记录错误的文档长度和描述这个语言的文档长度相当的语言,你学习它时会有着很陡峭的学习曲线。
别发愁!不懂sed不是你的错,我将在下面介绍sed的方方面面,但我介绍sed的那些特性时,我会以我学习它们的顺序来介绍,我并不是一下把它们全学会了,你也不需要一下全学会。
非常重要的命令:做替换的s
Sed有几个命令,但大多数人只学了一个替换命令:s。替换命令替换所有匹配正则表达式的值为一个新值,一个简单的例子,它将在”old”文件中的”day”替换为”night”后存入”new”文件中。
sed s/day/night/ new
或是另一种方式(对于Unix新手来说),
sed s/day/night/ new
如果你想测试一下功能,你可以用这个命令:
echo day | sed s/day/night/
这将会输入”night”
我没有给参数加引号是因为在这个例子中不需要加,但是我建议你使和引号。如果你在命令中有元字符,引号是必要的,我了为强调这是一个好的习惯,我会在下面的示例中都加上单引号,如果加上引号,那么上例就是:
sed 's/day/night/' new
我必须强调的是sed编辑器会严格执行你的命令,如果你执行
echo Sunday | sed 's/day/night/'
这将会输出”Sunnight”因为它发现输入有”day”这个字符串。
替换命令有四个组成部分:
s
/http://www.cnblogs.com/
day
night
查找模式在左边,替换字符串在右边。
我们已经学习了引用和正则表达式。那是学习替换命令90%的工作量。换句话说,你已经知道90%最常用的sed使用方法,但仍有一些未来的sed高手需要了解的内容(你已经看了第1节,还有63节就看完了。),噢。……,如果你没看完,那么你就收藏这个页面吧。
用&作为匹配的串
有时你想查找一个模式,然后加上几个字符,比如在匹配的串前后加上括号。如果你是找一个确定的字符串,还是比较简单的,如下即可:
sed ‘s/abc/(abc)/’ < old > new
如果你不是非常清楚你将找到的是串是什么,那你应该如果来利用你找到的串做某种替换呢?
答案就是你需要一个特定的字符”&”,它即指代匹配的模式
sed ‘s/[a-z]*/(&)/’ < old > new
你可以在替换时多次使用”&”,比如,你可以次每行开头的数字复制一次,如下:
% echo “123 abc” | sed ‘s/[0-9]*/& &/’
123 123 abc
让我再修正一下这个例子,sed会以贪婪的方式匹配第一个串。对于’[0-9]*’的第一个区配是第一个字符,因为这个正则是匹配0个或多个数字。所以如果输入是”abc 123”,输出也不会改变(当然,除了有一个空格在字母之前)。一个能保证能匹配一个数字的更好的复制数字的方法是:
% echo “123 abc” | sed ‘s/[0-9][0-9]*/& &/’
123 123 abc
字符串”abc”没有改变,因为它没有匹配正则表达式,如果你想在输出中删除”abc”,你必须扩展正则表达式来匹配行的其它的部分,并显式地用”(”,”)”和”\1”来指名,这就是下节的内容了。
用\1来指明匹配的部分
我已经在正则表达式那一章中介绍了”(” ”)”和”\1”的用法。现在复习一下,被转义的括号(即,有反斜杠在前面的括号)来记录正则表达的某一部分。”\1”是被记录的第一个模式,”\2”是第二个被记录的模式。Sed可以记录9个模式。
如果你想保存每行的第一个单词,并删除行的其它部分,你可以用下面的命令:
sed ‘s/\(\[a-z]*).*/\1/’
我应该再详细地解释一下。正则表达式是以贪婪的方式匹配。”[a-z]*”匹配0个或多个小写字母,它会尽量匹配更多的小写字母。”.*”会在第一个匹配后匹配0个或多个字符。因为第一个模式已经匹配了所有的小写字母,第二个模式会匹配剩下的字符,所以你使用命令:
echo abcd123 | sed ‘s/\([a-z]*\).*/\1/’
会输出”abcd”,而删除后面的数字。
如果你想调换两个单词的位置,你可记录两个模式,并改变它们的次序。
sed ‘s/\([a-z]*\) \([a-z]*\)/\2 \1/’
注意两个模式中间是有空格的。这个可保证找到两个单词。但是[a-z]*这种模式会匹配0个字母,如果你想至少匹配一个字母,你可以用下面的命令:
sed ‘s/\([a-z][a-z]*\) \([a-z][a-z]*\)/\2 \1/’
“\1”并不需要一定出现在替换串中(右部),它你也可以在查找的模式中(左部)。如果你想删除重复的单词,你可以用:
sed ‘s/\([a-z]*\) \1/\1/’
注意你最多使用9个值,从”\1”到”\9”
替换标志
你可以在最后一个分隔符后加上标志,这些标志可以指明当当有多个模式在一行中被匹配,应该如何替换,让我们来看看它们吧。
/g 全局替换
大多数Unix工具是以每次读一行的方式对文件进行操作,sed,默认也是以行为单位。如果你告诉它你想改变一个单词,它只会改变每行第一次出现的这个单词,你可能想改变行中的每个单词。比如,我想给一行的每个单词加上括号,如果用[A-Za-z]*这种模式,将不会匹配”won’t”这种单词,我们用另一种模式,”[^ ]”,它会匹配除空格外的一切串。是的,这将会匹配任何串,因为”*”表示0个或多个。现在的sed版本见到这个模式会不太开心,它会产生如”Output line to long”的错误或是一直运行。我将它视为一个bug,并已经报告给Sun公司了,作为一个暂时的解决方法,你在使用”g”标志时必须避免匹配空串。一个变通的方法是”[^ ][^ ]*”,下面的命令会将第一个单词加上括号。
如果你想能每一个单词进行改动,你可以用下面的变通方法:
sed ‘s/[^ ][^ ]*/(&)/g’ < old > new
sed 是递归的吗?
Sed ‘s/loop/loop the loop/g’ < old > new
这不会产生死循环。如果执行第二个”s”命令,它会去修改第一个”s”命令的输出结果,我将在下面告诉如何执行多个命令。
/1, /2 等等 指明哪个发生了
sed ‘s/\([a-zA-Z]*\) \([a-zA-Z]*\) /\1/’ < old > new
sed ‘s/[a-zA-Z]* //2’ < old > new
sed ‘s/[a-zA-Z]* /DELETED/2g’ < old > new
sed ‘s/[^ ]*//2’ < old > new
sed ‘s/[^:]*//2’ < /etc/passwd > /etc/password.new
sed ‘s/^\([^:]*\):[^:]:/\1::/’ < /etc/passwd > /etc/password.new
sed
sed ‘s/./&:/80’ < file > new
sed 's/^................................................................................/&:/' new
/p 打印
Sed默认打印每行。如果它做了替换,就会打印新的文本。如果你使用”sed -n”,它就不会打印,默认,会打印所有新产生的行。当使用”-n”选项时,使用”p”标志会打印修改过的串。下面的例子执行的效果与grep相同。
sed –n ‘s/pattern/&/p’ < file
用/w filename写入一个文件
在第三个分隔符后还可以跟别的标志,带上”w”标志可以指定一个接收修改数据的文件。下面的例子是将所有为偶数的数且后面为空格的行写入even文件。
sed –n ‘s/^[0-9]*[02468]’ /&/w even’ < file
在这个例子中,输出文件是不需要的,因为输出没有改变。在使用”w”标志时,你必须确信你在”w”和文件名有且只有一个空格。你可以在一个sed命令中打开10个文件,这样你可以将数据流拆分到不同的文件中,使用上例和下面将介绍的多个替换命令,你可以将一个文件根据最后一个数字拆分到十个文件。你还可以用这个方法将错误日志或调试信息写到一个特定文件中。
将替换标志结合
你可以将标志结合起来使用,当然”w”必须是最后一个标志,比如下面的例子:
sed –n ‘s/a/A/2pw /tmp/file’ < old > new
接下来我将介绍sed的选项和调用sed的不同方法。
参数和sed的调用
先前,我只使用了一个替换命令,如果你想做两次替换,并且不想去读sed手册,你可以用管道将多个sed连起来:
sed ‘s/BEGIN/begin/’ < old | sed ‘s/END/end/’ > new
这种写法使用了两个进程,但是sed大师在一个进程可以完成时,决不会用两个。
多个命令使用-e选项
将多个命令结合起来的一种方法是在每个命令前加上-e选项:
sed –e ‘s/a/A/’ –e ‘s/b/B/’ < old > new
在先前的例子中并不需要”-e”选项,因为sed知道至少会有一个命令,如果你给sed一个参数,它必须是一个命令,并且sed处理来自标准输入的数据。
命令行中的文件名(s)
你可以在命令行中指定你想要的文个,如果在没有使用选项的情况下,有多于一个参数传递给sed,它必须是一个文件名,下面的例子是在三个文件中数不以”#”开头的行数。
sed ‘s/^#.*//’ f1 f2 f3 | grep –v ‘^$’ | wc -l
sed替换命令会将以”#”开头的行替换为空行,grep用于过滤空行,wc计算剩余的行数,sed还有别的命令可以不用grep来完成这个功能,我过会再介绍。
当然你可以用”-e”选项来执行上面的例子:
sed –e ‘s/^#.*//’ f1 f2 f3 | grep –v ‘^$’ | wc –l
sed还有两个选项没介绍。
sed –n:不打印
使用“-n”选项后,除非明确指明要打印,否则不打印任何内容。我在前在提到过”/p”标志可以让它又可以打印。让我再讲清楚点,命令:
sed ‘s/PATTERN/&/p’ file
如果PATTERN没在文件中出现,这个命令表现的就和cat一样,即没有内容被改变。如果有PATTERN在文件中出现,那么每行被打印两次。加上”-n”选择,如下面的这个例子就如同grep命令:
sed –n ‘s/PATTERN/&/p’ file
sed –f scriptname
如果你有大量的sed命令,你可以把它们放到一个文件中,再用下面命令执行:
sed –f sedcript < old > new
# sed comment - This script changes lower case vowels to upper case
s/a/A/g
s/e/E/g
s/i/I/g
s/o/O/g
s/u/U/g
在shell脚本中的sed
sed -e 's/a/A/g' \
在C Shell中引用多行sed
#!/bin/csh -f
sed 's/a/A/g
s/e/E/g \
s/i/I/g \
s/o/O/g \
s/u/U/g'
在Bourne shell中引用多行sed
在Bourne shell中不用加上斜杠,就可以引用多行:
#!/bin/sh
sed '
s/a/A/g
s/e/E/g
s/i/I/g
s/o/O/g
s/u/U/g'
sed 脚本
另一个执行sed的方法是用脚本,创建一个脚本名为CapVowel,并给它执行权限,内容如下:
#!/bin/sed -f
s/a/A/g
s/e/E/g
s/i/I/g
s/o/O/g
s/u/U/g
CapVowel < old > new
注释
Sed注释是第一个非空白字符是”#”的行,在许多系统上,sed只能一个注释,并且它必须是脚本的第一行。在Sun上(在1988年我写这篇文章时),你可以在脚本的任何地方写注释。现代的版本的sed支持这种写法。如果第一行以”#n”开头,那么它和使用”-n”选项的效果相同:默认关闭打印。但这种写法在sed脚本行不通,因为在sed脚本中第一行必须以”#!/bin/sed -f”开头,因为我认为”#!/bin/sed -nf”会产生一个错误。在我2008年尝试这种写法的时候还是不能工作的,因为sed会认为”n”是文件名,但是:
“#!/bin/sed -nf”
却是可以工作的。
将参数传递到sed脚本
如果你还记得my tutorial on the Unix quoting mechanism,将一个词传递到shell脚本,再调用是sed很简单的。复习一下,你可以用单引号可以打开引用或关闭引用。下面是一个很简单的脚本来模拟grep:
#!/bin/sh
sed –n ‘s/’$1’/&/p’
但是这个脚本有一个问题,如果你将空格作为参数,这个脚本会引起一个语法错误,一个更好的版本可以防止这种情况的发生。
#!/bin/sh
sed -n 's/'"$1"'/&/p'
将上面的内容保存到sedgrep文件中,你可以键入:
sedgrep '[A-Z][A-Z]'
这可以让sed表现地和grep命令一样。
在脚本中使用sed之here document
你可以用sed提示用户参数输入,然后用这些参数创建一个文件将这些参数填进去。你可创建一个有些待替换的值的文件,然后用sed来改变这些值。一个比较简单的方法是用”here is”是用here document,它将shell脚本当作标准输入使用。
#!/bin/sh
echo -n 'what is the value? '
read value
sed
The value is XXX
EOF
当执行之后,脚本会提示:
What is the value?
如果你输入”123”,下一行就是:
The valu is 123
我承认这是一个牵强的例子,here document可用不用来sed来做,下面的例子完成的是相同的任务。
#!/bin/sh
echo -n 'what is the value? '
read value
cat <<EOF
The value is $value
EOF
但是结合sed在here document中可以完成一些复杂的操作,注意:
sed ‘s/XXX/’$vallue’/’ << EOF
会给出语法错误,如果用户输入空格,下面的写法更好:
sed ‘s/XXX/’”$value”’/’ << EOF
多个命令和执行顺序
随着我们的学习,sed命令将变的更加复杂,并且真正的执行顺序会变得让人糊涂。其实它是很简单的,先读取一行,每个命令按用户指定的顺序执行来对输入的行操作。在替换之后,下一个命令将会在相同的行上操作,但这一行是上一命令修改过的行。无论你何时有疑问,最好的方法就是创建一个小例子来试一个。当你写一个复杂的命令不能工作时,就设法使它简单。如果你不能使一个复杂的脚本工作,就将它拆成两个小的脚本,然后用管道将两个脚本连起来。
文本的位置和区间
你已经学习了一个命令了,但你已经领教了sed的强大,然而,它现在做的只是grep加上替换。即是每个替换命令只关心一行,而不管附近几行。如果可以有限制在特定的一些行内操作,那该多有用呀。一些有用的限制可以是:
- 通过行号指定某行。
- 通过行号指过行的区间。
- 所有包含某一模式的行。
- 从文件开始到某一正则的所有行。
- 从某一正则到文件结尾的所有行。
- 在两个正则之间的所有行。
Sed不但可以完成上述功能,而且可以做得更好,在sed中的每个命令都可以指定它的操作位置,区间或像上面所列的功能,命令操作范围的限制是下面的格式:
用行号限定
最简单的限制是用行号,如果你想删除第3行的第一个数字,只需要在命令前加3。
sed ‘3 s/[0-9][0-9]*//’ < file > new
模式
许多Unix工具,如vi和more用斜杠来搜索一个正则表达式。Sed遵循这一传统,你可以用一斜杠来结束正则表达式。下面的例子是删除以”#”开头的所有行的第一个数字。
sed ‘/^#/ s/[0-9][0-9]*//’
我在”/expression/”之后写了一个空格是为了方便阅读,它并不是必要的,但是不写空格,这个命令就比较难看懂,sed还提供了一些别的指定正则表达式的方法,但这个我稍后再讲,如果一个表达式以一个反斜杠开头,则下一个字符是一个分隔符,用逗号代替斜杠的写法如下:
sed ‘\,^#, s/[0-9][0-9]*//’
这种写法最大的好处是搜索斜杠,假设你想搜索”/usr/local/bin”,替换为”/common/all/bin”,你可以用反斜杠来转义斜杠:
sed '/\/usr\/local\/bin/ s/\/usr\/local/\/common\/all/'
如果你用下划线而不是斜杠来作为分隔符会更好理解一些,下面的例子在搜索命令和替换命令中都使用下划线做为分隔符:
sed '\_/usr/local/bin_ s_/usr/local_/common/all_'
这就解释了为什么sed脚本有隐晦的名声。我可以举出来一个恶心的例子:搜索所有以”g”开头的行,并将行中每个”g”替换为”s”:
sed '/^g/s/g/s/g'
用一个空格将命令分开,并在替换命令中用下划线分隔会更容易理解:
sed '/^g/ s_g_s_g'
嗯...,我收回我说的,它还是不好懂。这就是一个教训,如果在SunOS下写sed脚本,加上注释,你可以在别的操作系统上运行时删除它,你现在应该知道删除注释的方法了吧?注释是一件好事,当你写脚本时,你也许会完全理解脚本的含义,但过六个月之后,没有注释,它就是一堆乱码。
用行号限定范围
你可以用逗号分隔的行号指定范围,如果要限制替换在前100行,你可以用:
sed '1,100 s/A/a/'
如果你准确地知道一个文件中有多少行,你可以显式地说明处理剩余的行,如果是这样,假设你用wc命令知道这个文件有532行:
sed '101,532 s/A/a/'
一个更简单的办法是用一个特殊的字符”$”,它表示文件的最后一行。
sed '101,$ s/A/a/'
“$”符号表示最后也是一个传统,在cat –e vi和ed工具中都是这样,用cat –e就将会多个文件行号累记,即:
sed '200,300 s/A/a/' f1 f2 f3 >new
等价于:
cat f1 f2 f3 | sed '200,300 s/A/a/' >new
用模式限定范围
你可以用两个正则表达式限定范围,假设以”#”开头的是注释,你想找一个关键词,你移除所有注释直到你看到第二个关键词,假设两个关键词是”start”和”stop”:
sed '/start/,/stop/ s/#.*//'
第一个模式告诉sed开始对每行进行替换操作,第二个模式告诉sed停止对剩下的行进行操作。如果”start”和”stop”这两个模出现了两次,那么替换也会进行两次,如果”stop”模式没有匹配,那么替换就会对余下的所有行进行匹配。
你应该了解如果”start”模式如果找到了,替换就在包含”start”的行进行替换,它就像打开了一个面向行的开关,然后下一行被读入,进行替换,当然如果它包含”stop”,这个开关就关闭了,开头是面向行的,它并不是面向词的。
你可以结合行号和正则表达式来限定,下面的例子是从开始移除注释直到它找到”start”关键词:
sed -e '1,/start/ s/#.*//'
下面的例子展示了移除两个关键词以外的注释:
sed -e '1,/start/ s/#.*//' -e '/stop/,$ s/#.*//'
最后一个例子有一个重叠的区间”/start/,/stop/”,因为两个范围都在包含关键词的行上操作,我将会告诉你一行完成这个功能的方法。
在我继续介绍更多命令之前,我应该告诉你一些命令不能对一个范围内的行操作,在我介绍这些命令时,我会告诉你,在下节中我将会讲三个不能对一个范围的行进行操作的命令。
用d删除
使用范围限定容易让人糊涂,所以你在写一个新的脚本时,你应该先试试。一个删除符合限定行的命令是:”d”。如果你想看文件的前10行,你可以用:
sed '11,$ d'
它与head命令的功能很像。如果你想删除一个信件的头部信息,也就是删除从文件开始直到一个空行的所有内容,可以用:
sed '1,/^$/ d'
你还可以模拟tail命令,假设你知道文件的长度,wc命令可以统计行数,expr可以对文件行数减10.一个Bourne Shell想查看文件最后10行,可许可以这样写:
#!/bin/sh
#print last 10 lines of file
# First argument is the filename
lines=`wc -l $1 | awk '{print $1}' `
start=`expr $lines - 10`
sed "1,$start d" $1
也可以用正则表达式来标记删除操作的开始和结束位置,或者也可以只用一个正则表达式,删除所有以”#”开头的行很容易:
sed '/^#/ d'
删除注释和空行需要两个命令,第一个删除从”#”到行尾的字符,第二个命令删除所有的空行:
sed -e 's/#.*//' -e '/^$/ d'
第三个例子再增加一个功能:删除所有行前面的空格和tab:
sed -e 's/#.*//' -e 's/[ ^I]*$//' -e '/^$/ d'
“^I”字符是一个CTRL-I或是tab,你可以直接输入tab。注意上面操作的顺序,这样写是出于一个合理的理由,注释可能在前面有空格的一行的中间出现,所以注释第一个个被删除,这样就只剩空格在前面了,第二个命令就可把这样空格删除,最后一个命令会把空行删除了。
这展示了sed用模式空间来操作一行,sed真正的操作是:
1. 将输入行复制到模式空间。
使用第一个sed命令。
2.
3.
4.
用p打印
另一个有用的命令是打印命令”p”,如果sed不以”-n”开头,那么”p”会重复输入,命令:
sed ‘p’
会重复会一行,如果你想每行打印两次,用:
sed '/^$/ p'
加上”-n”选项会关闭打印,除非你明确要求要打印。另一个模拟head功能的方法是只打印你想要的行,下面的例子打印前10行。
sed -n '1,10 p'
sed也可以完成grep的功能,打印匹配正则表达式的行:
sed -n '/match/ p'
它等价于:
grep match
用!对限定取反
有时你想对匹配一个正则表达式以外的行进某个操作,或是限定范围之外的行。”!”字符,在Unix系统中意义为”非”,对位置限定取反。你应该记得:
sed -n '/match/ p'
它的功能和grep相似,grep命令中的-v操作表示不包括某一模式,sed可以用下面的方式做到这一点:
sed -n '/match/ !p'
d,p和!之间的关系
正如你所注意到的一样,sed可以用多种方式来解决同一问题,这是因为print和delete有着相反的功能,似乎”!p”和”d”的功能相似,且”!d”和”p”功能相似,为这测试这一点,我建了一个20行的文件,尝试每一种组合,下表就是执行的结果,以来展示它们的差异:
d,p和!之间的关系
Sed
这个表表明下面的命令是相似的:
sed -n '1,10 p'
sed -n '11,$ !p'
sed '1,10 !d'
sed '11,$ d'
它还显示了”!”命令对位置区间取反了,命令在取反后的空间上操作。
q退出命令
还有一个简单的命令可以限定操作在一个指定范围内,它就是”q”命令,第三种模拟head命令的方法是:
sed '11 q'
当执行到第11行时,就会退出,它个命令在你想到达某种条件后就退出编辑时特别有用。
“q”命令是一个不接受范围限定的命令,很显然下面的命令:
sed '1,10 q'
无法退出10次,相反:
sed '1 q'
或:
sed '10 q'
是正确的。
用{和}组合
大括号”{”和”}”在sed中用来组合命令。
这个用法不太难,但是有一个难点,因为sed中的每个命令必须独起一行,下面的大括号和内嵌的sed命令必须在不同的行。
在前面,我向你介绍了如何移除了以”#”的注释,如果你想限定删除所有在”begin”和”end”关键词之间的行,你可以用:
#!/bin/sh
# This is a Bourne shell script that removes #-type comments
# between 'begin' and 'end' words.
sed -n '
'
大括号是可以嵌套的,这也可以使你结合位置范围,你可以进行上面相同的操作,但只限操作前100行:
#!/bin/sh
# This is a Bourne shell script that removes #-type comments
# between 'begin' and 'end' words.
sed -n '
'
你可以在一个大括号前写”!”,它可以反转地址范围,下面的例子是移除在begin和end之间范围外的所有注释:
#!/bin/sh
sed '
'
用’w’命令写一个文件
你也许还记得替换命令可以写入一个文件,这是另一个例子,它是将偶数开头(后面有一个空格)的行写入文件:
sed -n 's/^[0-9]*[02468] /&/w even'
我在命令的替换部分用&,这样行就不会被改。一个更简单的例子是用”w”命令,它和上面的例子有相同的功能:
sed -n '/^[0-9]*[02468]/ w even'
切记:命令后只能有一个空格,剩余的部分将会被当作是文件名。”w”命令也有相同的局限:最多只能打开10个文件。
用’r’命令读一个文件
这也是一个读文件的命令,命令:
sed '$r end' out
会将”end”文件追加到out尾部(位置”$”),下面的命令会在有”INCLUDE”单词的行后插入文件:
sed '/INCLUDE/ r file' out
你可以用大括号来删除有”INCLUDE”的行:
#!/bin/sh
sed '/INCLUDE/ {
}'
#!/bin/sh
# this example is WRONG
sed -e '1 {
}'
% include 'sys/param.h' file.c.new
#!/bin/sh
# watch out for a '/' in the parameter
# use alternate search delimiter
sed -e '\_#INCLUDE <'"$1"'>_{
}'
Test first file
#INCLUDE
Test second file
#INCLUDE
sed_include1.sh file1
sunOS和#注释命令
#!/bin/sh
# watch out for a '/' in the parameter
# use alternate search delimiter
sed -e '\_#INCLUDE <'"$1"'>_{
}'
添加,修改,添加新行
Sed有三个命令可以添加新行到输出流,因为一个整行要添加,这个新行必须在单独的一行,这是必须的。如果你注释许unix工具,你会期待sed遵循相同的传统:用”\”表示一行的继续,这个语法像”r”和”w”命令一样,也是很严格的。
用’a’命令追加一行
“a”命令用于在一个模式范围之后追加一行,下面的例子可以在每个有”WORD”的行后添加一行:
#!/bin/sh
sed '
/WORD/ a\
Add this line after every line with WORD
'
如果你高兴,你也可以在shell中把它写成两行:
#!/bin/sh
sed '/WORD/ a\
Add this line after every line with WORD'
用i插入一行
你可以在一个模式前用i插入一个命令:
#!/bin/sh
sed '
/WORD/ i\
Add this line before every line with WORD
'
用’c’改变一行
你可以用一个新行改变当前行。
#!/bin/sh
sed '
/WORD/ c\
Replace the current line with the line
'
一个”a”命令跟在”d”命令之后是不会被执行的,原因已经说过了,因为”d”命令会中止以后的所有操作,你可以用大括号将这个操作结合起来:
#!/bin/sh
sed '
/WORD/ {
i\
Add this line before
a\
Add this line after
c\
Change the line to this one
}'
在sed脚本中开头的tab和空格
Sed会忽略所有命令开头的tabs和空格,但是空格开头的行如果后面是”a”,”c” 或是”i”命令,它们可能被忽略也可能不被忽略,在SunOS上,两种都有,Berkeley(和linux)方式的sed是在/usr/bin下,AT&T版本(System V)是在/usr/5bin/。
#!/bin/sh
sed '
\
'
添加多行
上面三种命令都允许你一次添加多行,只需要在每行后面加上一个”\”:
#!/bin/sh
sed '
/WORD/ a\
Add this line\
This line\
And this line
'
添加行和模式空间
我前面已经提到过模工空间,大多数命令在模式空上操作,其后的命令在上一次改动的结果上操作。前面三个命令,比如说读文件命令,添加新行到输出流去,这个过程跳过了模式空间。
位置范围和上面的命令
你也许还记得我在前面警告过你一些命令可以处理多行,而有一些命令则不可以。更准确地说:命令:”a”,”i”,”r”和”q”不可以接受一个范围,比如”1,100”或是”/begin/,/end/”。文档上说read命令可以接受一个范围,但我在尝试的时候却出错了,”c”命令,即修改命令可以接受一个范围,并且允许你将多行改为一行。
#!/bin/sh
sed '
/begin/,/end/ c\
***DELETED***
'
#!/bin/sh
# add a blank line after every line
sed '1,$ {
}'
多行模式
多数Unix工具都是面向行的,正则表达式也是向面行的。搜索跨多行的模式不是一件容易的事(提示,它会非常短)。
- 从输入文件读取一行,将它放入模式空间。如果当前文件到达文件结束处,还有其它文件要读,将当前文件关闭,将下一个文件打开,将新的文件的第一行读入模式空间。
- 文件行加1。打开一个新文件不会重置文件行数。
- 检查每一个sed命令,如果在命令前有一个限制,并且在模式空间的当前行符合这一限制,命令就会被执行,一些命令,如”n”或”d”会导致sed返回第1步,”q”命令会导致sed中止,否则下一条命令继续执行。
- 在所有命令被检查执行后,除非使用了”-n”参数,sed就将模式空间输出。
在命令前的限制会决定命令是否会被执行,如果限制是一个模式,并且命令是删除命令,下面的例子就是删除所有包含模式的行:
/PATTERN/ d
如果限制是一对行号,那么删除操作会对这个开区间内所有行进行删除。
10,20 d
/begin/,/end/ d
# what happens if the second number
# is less than the first number?
sed -n '20,1 p' file
和
# generate a 10 line file with line numbers
# and see what happens when two patterns overlap
yes | head -10 | cat -n | \
sed -n -e '/1/,/7/ p' -e '/5/,/9/ p'
AB
CD
EF
GH
IJ
+----------------+---------+------------------------------------------+
|Pattern
|Space
+----------------+---------+------------------------------------------+
|AB
|AB
|AB
+----------------+---------+------------------------------------------+
用=打印行号
“=”命令将当前行号输出到标准输出,其中一个作用就是找到包含某模式的行号:
# add line numbers first,
# then use grep,
# then just print the number
cat -n file | grep 'PATTERN' | awk '{print $1}'
sed -n '/PATTERN/ =' file
#!/bin/sh
lines=`wc -l file | awk '{print $1}' `
#!/bin/sh
lines=`sed -n '$=' file `
#!/bin/sh
# Just print the line numbers
sed -n '/begin/,/end/ {
=
d
}' file
用y变换
sed 'y/abcdef/ABCDEF/' file
sed '/0x[0-9a-zA-Z]*/ y/abcdef/ABCDEF' file
用”l”打印控制字符
“l”命令可以打印当前模式空间,所以用它来调试sed是很有效的。而且它可将一个不可见字符转为可打印字符,转换规则是”\”加上字符八进制值。在探求sed的微妙的问题时,我感觉打印当前模式空间很有用。
在多行上工作
sed -e 'N'
#!/bin/sh
sed '
# look for a "#" at the end of the line
/#$/ {
# Found one - now read in the next line
# delete the "#" and the new line character,
}' file
#!/bin/sh
sed -n '
/ONE/ {
# found "ONE" - read in next line
# look for "TWO" on the second line
# and print if there.
}' file
#!/bin/sh
sed '
/ONE/ {
# append a line
# search for TWO on the second line
# found it - now edit making one line
}' file
#!/bin/sh
sed '
/ONE/ {
# append a line
# "ONE TWO" on same line
# "ONE
# TWO" on two consecutive lines
}' file
#!/bin/sh
sed '
/ONE/ {
# append a line
# if TWO found, delete the first line
}' file
#!/bin/sh
sed -n '
# by default - do not print anything
/ONE/ {
# append a line
# if TWO found, print the first line
}' file
#!/bin/sh
sed '
/ONE/ {
# append the next line
# look for "ONE" followed by "TWO"
#
#
#
}' file
#!/bin/sh
sed '=' file | \
sed '{
}'
0x1fff table2
0x1fff
table2
#!/bin/sh
tr ' ' '\012' file|
sed ' {
}'
#!/bin/sh
sed '
s/ /\
/' | \
sed ' {
}'
#!/bin/sh
sed 's/ /\\\
/' file
#!/bin/csh -f
sed '\
s/ /\\\\
/' file
+----------------+---------+------------------------------------------+
|Pattern
|Space
+----------------+---------+------------------------------------------+
|AB
|AB
|AB
|AB
|AB
|AB
+----------------+---------+------------------------------------------+
|AB\nCD
|AB\nCD
|AB\nCD
|AB\nCD
|AB\nCD
|AB\nCD
+----------------+---------+------------------------------------------+
在sed脚本中使用换行
(echo a;echo x;echo y) | sed '/x$/ {
N
s:x\n:x:
}'
a
xy
(echo a;echo x;echo y) | sed 's:x:X\
:'
a
X
y
持有缓存
用”x”交换
“x”命令用来交换(eXchange)模式空间和持有缓存,”x”命令单独使用没什么用,执行sed命令:
sed 'x'
上下文grep的例子
#!/bin/sh
# grep3 - prints out three lines around pattern
# if there is only one argument, exit
case $# in
esac;
# I hope the argument doesn't contain a /
# if it does, sed will complain
# use sed -n to disable printing
# unless we ask for it
sed -n '
'/$1/' !{
}
'/$1/' {
---
}'
grep3 vt100
用h或H持有
#!/bin/sh
# grep3 version b - another version using the hold commands
# if there is only one argument, exit
case $# in
esac;
# again - I hope the argument doesn't contain a /
# use sed -n to disable printing
sed -n '
'/$1/' !{
}
'/$1/' {
---
}'
在持有buffer中保存多行
#!/bin/sh
# print previous entry
sed -n '
/^[ ^I]/!{
---
}
# what about lines that start
# with a space or tab?
/^[ ^I]/ {
}'
#!/bin/sh
# grep4: prints out 4 lines around pattern
# if there is only one argument, exit
case $# in
esac;
sed -n '
'/$1/' !{
}
'/$1/' {
---
}'
使用g或G获得
#!/bin/sh
# grep3 version c: use 'G'
# if there is only one argument, exit
case $# in
esac;
# again - I hope the argument doesn't contain a /
sed -n '
'/$1/' !{
}
'/$1/' {
---
}'
#!/bin/sh
# change the first hex number to upper case format
# uses sed twice
# used as a filter
# convert2uc out
sed '
s/ /\
/' | \
sed ' {
}'
#!/bin/sh
# convert2uc version b
# change the first hex number to upper case format
# uses sed once
# used as a filter
# convert2uc out
sed '
{
}'
#!/bin/sh
# convert2uc version b
# change the first hex number to upper case format
# uses sed once
# used as a filter
# convert2uc out
sed '
{
}'
流程控制
例子中记录段落,并且如果它包含某一模式(由参数指定),脚本就会被整段打印。
#!/bin/sh
sed -n '
# if an empty line, check the paragraph
/^$/ b para
# else add it to the hold buffer
H
# at end of file, check paragraph
$ b para
# now branch to end of script
b
# this is where a paragraph is checked for the pattern
:para
# return the entire paragraph
# into the pattern space
x
# look for the pattern, if there - print
/'$1'/ p
'
用”t”测试
sed 's/([ ^I]*)/g'
只能移除最内层的空格,你也许需要管道四次来删除每层的空格,你可能会用正则表达式:
sed 's/([ ^I()]*)/g'
但这又会把括号集合删除。”t”命令可以来解决这个问题
#!/bin/sh
sed '
:again
'
添加注释的另一种方式
#!/bin/sh
sed '
/begin/ {
0i\
}'
有着糟糕文档的”;”
#!/bin/sh
sed -n '
'/$1/' !{;H;x;s/^.*\n\(.*\n.*\)$/\1/;x;}
'/$1/' {;H;n;H;x;p;a\
---
}'
将正则表达式作为参数传递。
#!/bin/sh
sed 's/'"$1"'//g'
#!/bin/sh
arg=`echo "$1" | sed 's:[]\[\^\$\.\*\/]:\\\\&:g'`
sed 's/'"$arg"'//g'
命令总结
+---------------------------------------------------------+
结论
=================================================================================================
================================================================================================
sed的命令是针对行的
也就是,如果没有g那就是只替换每一行中第一个符合匹配的字符串
如果加上g,就是把所有行中匹配的字符串替换
/number
表示替换第几个匹配的。如/2替换第2个匹配的
[gxolc@zxdev:/ztesoft/gxolc/liuxx]$cat sedtest
aaa bbb aaa ccc aaa ddd
aaa bbb aaa ccc aaa ddd
aaa bbb aaa ccc aaa ddd
aaa bbb aaa ccc aaa ddd
aaa aaa aaa aaa aaa aaa
[gxolc@zxdev:/ztesoft/gxolc/liuxx]$cat sedtest|sed 's/aaa/111/2'
aaa bbb 111 ccc aaa ddd
aaa bbb 111 ccc aaa ddd
aaa bbb 111 ccc aaa ddd
aaa bbb 111 ccc aaa ddd
aaa 111 aaa aaa aaa aaa