sed2:命令

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/CPP_MAYIBO/article/details/84500569

概要

sed 程序是由一个或者多个sed command组成,由一个或多个的-e,-f, --expression, 和–file 选项来传递。如果不使用这些选项,那么第一个非选项参数将会被当做sed脚本

sed命令的语法格式

[addr]X[options]

X表示一个单字符的sed 命令,[addr]表示一个可选的行位置信息,如果指定了[addr],那么命令X只会在匹配的行上执行。[addr]可以是一个单独的行数子、一个正则表达式或是一个行的范围。[options]是被一些sed 命令使用的。如:

  1. 下面命令将删除输入文件中30到35行的内容,30,35 表示一个行地址范围
sed ’30,35d’ input.txt
  1. 下面命令会一直打印输入,直到找到以单词foo开头的输入,然后sed会退出,退出状态是42,如果直到文件结束都没有找到匹配的输入,那么sed将会以0作为退出状态。 /^foo/ 是一个正则表达式,q是退出命令,42是命令选项
sed ’/^foo/q42’ input.txt > output.txt
  1. 脚本或者脚本文件中的多个命令可以使用分号(;)或者换行符来分隔。多个脚本可以使用 -e 或者-f 选项指定,下面示例中的命令都是等价的:
sed ’/^foo/d ; s/hello/world/’ input.txt > output.txt
 
sed -e ’/^foo/d’ -e ’s/hello/world/’ input.txt > output.txt
 
echo ’/^foo/d’ > script.sed
echo ’s/hello/world/’ >> script.sed
sed -f script.sed input.txt > output.txt
 
echo ’s/hello/world/’ > script2.sed
sed -e ’/^foo/d’ -f script2.sed input.txt > output.txt

命令 s 的使用

在sed众多的命令中,s应该是最长使用的命令,没有之一,命令 s 的语法如下:

's/regexp/replacement/flags'

说明:

  • 在replacement中可以使用 \n (n是一个从1到9的数字)引用,该引用表示在regexp中由括号 ( 和 ) 括起来的部分,\1 表示由第一组 (、)括起来的部分,\2表示第二组 (、)括起来的部分,以此类推;另外可以在replacement使用符 & 来代替整个regexp匹配的部分;如下示例:
$ printf 'abc123\n' | sed -n 's/\([a-z]*\)[0-9]*/\1/p'   
abc
$ printf 'abc123\n' | sed -n 's/\([a-z]*\)\([0-9]*\)/\2/p'
123

$ printf 'abc123\n' | sed -n 's/\([a-z]*\)[0-9]*/&/p' 
abc123
  • 分隔符“/”,可以由任意其他的单个字符替换,如果分隔符出现在 regexp或者replacement部分中,则需要在其前面使用反斜杠 “\”进行转义 ,如下示例:
$ printf 'abc123\n' | sed -n 's:\([a-z]*\)[0-9]*:&:p'     
abc123
$ printf 'abc:123\n' | sed -n 's:\([a-z]*\)\:[0-9]*:&:p'
abc:123

上面示例中,第一条命令使用冒号(:)作为分隔符代替斜杠(/),第二条命令在regexp中出现了分隔符 (:),这时就需要在前面使用反斜杠了。

  • 在GNU版的sed的扩展功能中,我们可以使用一些由反斜杠和字母(L, l, U, u, E中的一个)组成的特殊序列表示一下特定的含义,具体如下:
    \L 表示将replacement转换为小写,直到遇到一个 \U 或者 \E
    \l 表示将下一个字符变为小写
    \U 表示将replacement转换为大写,直到遇到一个 \L 或者 \E
    \u 表示将下一个字符变为大写
    \E 用于终止由 \U 或者 \L启动的字母大小写转换
    示例:
$  printf 'aabbcc\n' | sed 's/[a-Z]*/\U&/'
AABBCC
$  printf 'aabbcc\n' | sed 's/[a-Z]*/\u&/'
Aabbcc
$  printf 'AABBCC\n' | sed 's/[a-Z]*/\L&/'      
aabbcc
$  printf 'AABBCC\n' | sed 's/[a-Z]*/\l&/'
aABBCC
$  printf 'AABBCC\n' | sed 's/\([A-Z]\)[A-Z]*/\L\1\E&/'
aAABBCC

替换标记

s 命令可以使用一个或多个的替换flag,如下:

  • g 表示替换行内全部的匹配
  • number(数字n) 表示只替换行内的第n个匹配
  • w filename 表示可以将输出写入到指定的文件中

如下:

$  printf 'AABBCC\n' | sed 's/[a-Z]/123/4'
AAB123CC
 
$  printf 'AABBCC\n' | sed 's/[a-Z]/123/g'
123123123123123123
 
printf  'AABBCC\n' | sed -n 's/[a-Z]/123/g'
$  printf  'AABBCC\n' | sed -n 's/[a-Z]/123/gp'
123123123123123123
 
printf  'AABBCC\n' | sed -n 's/[a-Z]/123/gw 123.txt'
$ cat 123.txt 
123123123123123123

我们可以使用 number(N)和g的组合来使得替换从第N处开始,而不仅仅是指替换第N处:

$ printf 'AABBCC\n' | sed 's/[a-Z]/123/4g'
AAB123123123

还有一个替换标记e,它可以将替换后的模式空间中的内容当做一个shell命令来执行,然后使用执行的结果替换模式空间的内容:

$  printf  'AABBCC\n' | sed -n 's/[a-Z]*/date/ep'
2018年 11月 18日 星期日 17:12:05 CST
$  printf  'AABBCC\n' | sed -n 's/[0-9]*/date/ep'
sh: dateAABBCC: command not found
 
$ 

上面示例中,第一个命令中,替换后的结果是date,所以最后输出的是date命令的执行结果,第二个命令中,由于替换后的结果是dateAABBCC,这不是一个shell命令,所有会有错误产生,同时模式空间也变为空了,因为没有结果输出。

其他常用的命令

q [exit-code] 退出sed脚本的执行

$ seq 12 20 | sed 3q
12
13
14

d 删除模式空间内容,然后开始下一个循环

$ seq 1 5 | sed 3d  
1
2
4
5
$ seq 1 5 | sed 3,5d
1
2

p 打印模式空间到标准输出,该命令通常和 -n 选项一起使用

$ seq 1 5 | sed -n 3,5p
3
4
5

n 读入输入的下一行替换当前的模式空间内容,如果没有更多的输入读入则会退出sed,该命令通常用以跳过一些输入行,每隔 N 行进行处理,如下:

$ seq 1 10 | sed 'n;n;n;s/[0-9]*/HH/' 
1
2
3
HH
5
6
7
HH
9
10

上述命令中,每隔4行进行替换处理。
当然我们可以通过另一种方式来达到上面的目标,GNU sed提供了一种 first~step 的地址语法:

$ seq 1 10 | sed '0~4s/[0-9]*/HH/'
1
2
3
HH
5
6
7
HH
9
10

上述命令表示从第0行开始,每4行执行一次替换操作。

{commands} 可以将一组命令放入到由大括号{}中去执行,这通常用以对某个特地的地址或者地址范围执行多个命令时使用:

$ seq 1 10 | sed -n '2,5s/[0-9]*/HHH/;p'
1
HHH
HHH
HHH
HHH
6
7
8
9
10
$ seq 1 10 | sed -n '2,5{s/[0-9]*/HHH/;p}'
HHH
HHH
HHH
HHH

可以通过上面的示例来了解使用 {} 和不使用 {}的区别。

不太常用的命令

= 打印当前输入行的行号

$ head -5 /etc/passwd | sed '='
1
root:x:0:0:root:/root:/bin/bash
2
bin:x:1:1:bin:/bin:/sbin/nologin
3
daemon:x:2:2:daemon:/sbin:/sbin/nologin
4
adm:x:3:4:adm:/var/adm:/sbin/nologin
5
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

y/source-chars/dest-chars/ 将source-chars中的字符替换为dest-chars对应位置的字符

$ echo hello world | sed 'y/abcdefghij/0123456789/'
74llo worl3

上述命令中,就是将输入行中字符a替换为数字0,字符b替换为数字1,字符c替换为数字2,依次类推。因此source-chars的长度和dest-chars的长度必须相同

a text 或
a
text
在指定的输入行后添加内容

$ seq 1 3 | sed '2ahello world'
1
2
hello world
3
$ seq 1 3 | sed '2a\
> hello\
> world
> 3s/./HHH/'
1
2
hello
world
HHH

上面的示例都是在第二行后添加内容,如果需要添加多行内容,就要在每一行后使用一个反斜杠。如果不没有反斜杠,则表示添加内容的结束,如上的第二个命令。

i text 或
i
text
在指定的输入行之前插入内容

$ seq 1 3 | sed '2ihello world' 
1
hello world
2
3
$ seq 1 3 | sed '2i\           
> hello\
> world
> 3s/./HHH/'
1
hello
world
2
HHH

c text 或
c
text
替换指定行的内容

$ seq 1 10 | sed '2,9chello world' 
1
hello world
10
$ seq 1 10 | sed '2,9c\
> hello\
> world'
1
hello
world
10

从上面的示例中可以看到这里的替换并不是指一行一行的替换,而是把内容整体替换了指定的行范围内的内容。

上面的三个命令添加 a、插入 i、替换 c、都可以使用 -e 选项将命令和内容分隔为2个参数,如下:

$ seq 1 3 | sed  -e '2a\' -e hello     
1
2
hello
3
 
$ seq 1 3 | sed  -e '2i\' -e hello 
1
hello
2
3
 
$ seq 1 10 | sed  -e '2,9c\' -e hello
1
hello
10

r filename 读入文件filename的内容

$ seq 4|sed '2r/etc/passwd' 
1
2
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

R filename 按顺序一行一行的读入文件内容

$ seq 4|sed '2R/etc/passwd' 
1
2
root:x:0:0:root:/root:/bin/bash
3
4
 
$ seq 4|sed '2,4R/etc/passwd'
1
2
root:x:0:0:root:/root:/bin/bash
3
bin:x:1:1:bin:/bin:/sbin/nologin
4
daemon:x:2:2:daemon:/sbin:/sbin/nologin

w filename 将模式空间的内容写入到filename文件中

$ cat /etc/passwd | sed -n '/bash/w/tmp/test.txt'        
$ cat /tmp/test.txt                                
root:x:0:0:root:/root:/bin/bash
hadoop:x:500:500:hadoop:/home/hadoop:/bin/bash

上面命令将passwd文件中包含bash字符串的记录写入到文件test.txt文件中。

使用多个命令

如果要在一个sed语句中使用多个sed的命令,有如下几种方法:

1. 每一个命令使用一行

$ seq 6 | sed '
> 2d
> 4s/./HH/
> 6d'
1
3
HH
5

2. 使用-e选项

$ seq 6 | sed -e '2d' -e '4s/./HH/' -e'6d'
1
3
HH
5

3. 使用分号

$ seq 6 | sed -e '2d ; 4s/./HH/ ; 6d'        
1
3
HH
5

例外

有些命令是不能使用分号来分隔的,下面几种情形就不适合使用分号来分隔:

a,c,i(append/change/insert) 这三个命令之后所有内容都会被当做内容去添加、修改、插入:

$ seq 4 | sed  '2ahello; 3d'       
1
2
hello; 3d
3
4

可以看到分号后的命令也被当做内容添加了。

R,r,W,w 读写文件的时候,命令后面的部分都会被当做文件名

$ seq 2 | sed '1w hello.txt ; 2d'
1
2
 
$ ls -log
总用量 4
-rw-rw-r-- 1   2 11月 18 21:29 hello.txt ; 2d
 
$ cat hello.txt\ \;\ 2d 
1

e (command execution)

$ seq 2 | sed '1e mkdir test ;2d'
sh: 2d: command not found
1
2

e 命令是把其后面直到行尾的所有内容都发送给shell来执行,上面的示例中,首先会执行mkdir test创建目录,然后会把2d当做shell命令来执行,此时自然就会报错。

s///[we] 使用s命令替换时使用了 w 或 e 替换标记,原因和上述使用 w 命令 和 e 命令是一样的。

猜你喜欢

转载自blog.csdn.net/CPP_MAYIBO/article/details/84500569
今日推荐