awk
于 1977 年出生,今年 36 岁本命年,sed
比awk
大 2-3 岁,awk
就像林妹妹,sed 就是宝玉哥哥了。所以 林妹妹跳了个Topless
,他的哥哥sed
坐不住了,也一定要出来抖一抖。
sed
全名叫stream editor
,流编辑器,用程序的方式来编辑文本,相当的hacker
啊。sed
基本上就是玩正则模式匹配,所以,玩sed
的人,正则表达式一般都比较强。
同样,本篇文章不会说sed
的全部东西,你可以参看 sed
的手册 ,我这里主要还是想和大家竞争一下那些从手机指缝间或马桶里流走的时间,用这些时间来学习一些东西。当然,接下来的还是要靠大家自己双手。
用 s 命令替换
我使用下面的这段文本做演示:
1 | cat pets.txt |
把其中的my
字符串替换成Hao Chen’s
,下面的语句应该很好理解(s
表示替换命令,/my/
表示匹配my
,/Hao Chen’s/
表示把匹配替换成Hao Chen’s
,/g
表示一行上的替换所有的匹配):
1 | sed "s/my/Hao Chen's/g" pets.txt |
注意:如果你要使用单引号,那么你没办法通过\'
这样来转义,就有双引号就可以了,在双引号内可以用\"
来转义。
再注意:上面的sed
并没有对文件的内容改变,只是把处理过后的内容输出,如果你要写回文件,你可以使用重定向,如:
1 | sed "s/my/Hao Chen's/g" pets.txt > hao_pets.txt |
或使用 -i
参数直接修改文件内容:
1 | sed -i "s/my/Hao Chen's/g" pets.txt |
在每一行最前面加点东西:
1 | sed 's/^/#/g' pets.txt |
在每一行最后面加点东西:
1 | sed 's/$/ --- /g' pets.txt |
顺手介绍一下正则表达式的一些最基本的东西:
^
表示一行的开头。如:/^#/
以#
开头的匹配。$
表示一行的结尾。如:/}$/
以}
结尾的匹配。\<
表示词首。 如:\<abc
表示以abc
为首的詞。\>
表示词尾。 如:abc\>
表示以abc
結尾的詞。.
表示任何单个字符。*
表示某个字符出现了 0 次或多次。[ ]
字符集合。 如:[abc]
表示匹配 a 或 b 或 c,还有[a-zA-Z]
表示匹配所有的26
个字符。如果其中有^
表示反,如[^a]
表示非 a 的字符
正规则表达式是一些很牛的事,比如我们要去掉某html
中的tags
:
1 | <b>This</b> is what <span style="text-decoration: underline;">I</span> meant. |
看看我们的sed
命令
1 | 如果你这样搞的话,就会有问题 |
我们再来看看指定需要替换的内容:
1 | sed "3s/my/your/g" pets.txt |
下面的命令只替换第3
到第6
行的文本。
1 | sed "3,6s/my/your/g" pets.txt |
1 | cat my.txt |
只替换每一行的第一个 s:
1 | sed 's/s/S/1' my.txt |
只替换每一行的第二个s
:
1 | sed 's/s/S/2' my.txt |
只替换第一行的第3
个以后的s
:
1 | sed 's/s/S/3g' my.txt |
多个匹配
如果我们需要一次替换多个模式,可参看下面的示例:(第一个模式把第一行到第三行的my
替换成your
,第二个则把第3
行以后的This
替换成了That
)
1 | sed '1,3s/my/your/g; 3,$s/This/That/g' my.txt |
上面的命令等价于:(注:下面使用的是sed
的-e
命令行参数)
1 | sed -e '1,3s/my/your/g' -e '3,$s/This/That/g' my.txt |
我们可以使用&来当做被匹配的变量,然后可以在基本左右加点东西。如下所示:
1 | sed 's/my/[&]/g' my.txt |
圆括号匹配
使用圆括号匹配的示例:(圆括号括起来的正则表达式所匹配的字符串会可以当成变量来使用,sed
中使用的是\1,\2…
)
1 | sed 's/This is my \([^,&]*\),.*is \(.*\)/\1:\2/g' my.txt |
上面这个例子中的正则表达式有点复杂,解开如下(去掉转义字符):
- 正则为:
This is my ([^,]*),.*is (.*)
- 匹配为:
This is my (cat),……….is (betty)
然后:\1
就是cat
,\2
就是betty
sed 的命令
让我们回到最一开始的例子pets.txt
,让我们来看几个命令:
N 命令
先来看N
命令 —— 把下一行的内容纳入当成缓冲区做匹配。
下面的的示例会把原文本中的偶数行纳入奇数行匹配,而s
只匹配并替换一次,所以,就成了下面的结果:
1 | sed 'N;s/my/your/' pets.txt |
也就是说,原来的文件成了:
1 | This is my cat\n my cat's name is betty |
这样一来,下面的例子你就明白了,
1 | sed 'N;s/\n/,/' pets.txt |
a 命令和 i 命令
a
命令就是append
, i
命令就是insert
,它们是用来添加行的。如:
1 | 其中的1i表明,其要在第1行前插入一行(insert) |
我们可以运用匹配来添加文本:
1 | 注意其中的/fish/a,这意思是匹配到/fish/后就追加一行 |
下面这个例子是对每一行都挺插入:
1 | sed "/my/a ----" my.txt |
c 命令
c
命令是替换匹配行
1 | sed "2 c This is my monkey, my monkey's name is wukong" my.txt |
d 命令
删除匹配行
1 | sed '/fish/d' my.txt |
p 命令
打印命令
你可以把这个命令当成grep
式的命令
1 | 匹配fish并输出,可以看到fish的那一行被打了两遍, |
几个知识点
好了,下面我们要介绍四个sed
的基本知识点:
Pattern Space
第零个是关于-n
参数的,大家也许没看懂,没关系,我们来看一下sed
处理文本的伪代码,并了解一下Pattern Space
的概念:
1 | foreach line in file { |
Address
第一个是关于address
,几乎上述所有的命令都是这样的(注:其中的!
表示匹配成功后是否执行命令)
1 | [address[,address]][!]{cmd} |
address
可以是一个数字,也可以是一个模式,你可以通过逗号要分隔两个address
表示两个address
的区间,参执行命令cmd
,伪代码如下:
1 | bool bexec = false |
关于address
可以使用相对位置,如:
1 | 其中的+3表示后面连续3行 |
命令打包
第二个是cmd
可以是多个,它们可以用分号分开,可以用大括号括起来作为嵌套命令。下面是几个例子:
1 | cat pets.txt |
Hold Space
第三个我们再来看一下 Hold Space
接下来,我们需要了解一下Hold Space
的概念,我们先来看几个命令:
g
: 将hold space
中的内容拷贝到pattern space
中,原来pattern space
里的内容清除G
: 将hold space
中的内容append
到pattern space\n
后h
: 将pattern space
中的内容拷贝到hold space
中,原来的hold space
里的内容被清除H
: 将pattern space
中的内容append
到hold space\n
后x
: 交换pattern space
和hold space
的内容
这些命令有什么用?我们来看两个示例吧,用到的示例文件是:
1 | cat t.txt |
第一个示例:
1 | sed 'H;g' t.txt |
是不是有点没看懂,我作个图你就看懂了。
第二个示例,反序了一个文件的行:
1 | sed '1!G;h;$!d' t.txt |
其中的 '1!G;h;$!d'
可拆解为三个命令
1!G
—— 只有第一行不执行G
命令,将hold space
中的内容append
回到pattern space
h
—— 第一行都执行h
命令,将pattern space
中的内容拷贝到hold space
中$!d
—— 除了最后一行不执行d
命令,其它行都执行d
命令,删除当前行
这个执行序列很难理解,做个图如下大家就明白了:
就先说这么多吧,希望对大家有用。