Linux 学习与进阶之三剑客其一 SED

in Linux with 0 comment

Sed


SED是一种功能强大的流编辑器,Linux及Unix环境中现有的功能最强大的数据处理引擎之一,SED是stream editor的缩写,是一个流编辑器。

SedCut 类似,是一个面向行处理的命令,以“行”为单位进行流处理,处理后的结果输出到标准输出,默认情况下不会对处理文件进行修改,因此Sed命令一定程度上很安全。

一、命令结构

sed [options] 'command' file

执行过程:

参数
    -n:取消默认输出
    -i:直接将修改写入文件 (-i.bak 先备份再修改)
    -r:使用扩展正则
模式
    d:删除,即 delete
    a:追加,即 append
    i:插入,即 insert
    p:打印,即 print
    s:替换,即 substitue
    g:全局,即 global (仅用于替换模式)

中间部分是针对每行数据所要做的处理(操作),后接的文件是要处理的目标文件,如果忽略此参数,则 Sed 会把标准输入作为处理对象。

SED 一次处理一行内容。命令执行时会把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用命令会处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾,文件内容并没有实际改变。

举个栗子

例 1:

cat >>value.list<<'EOF'
#排名 名称 市值(亿美元) 所在国家 行业
NO. NAME VALUE(BillionDollars) Country-Industry
01 Apple 8858.88 USA CONSUMER ELECTRONICS
02 Google 8251.05 USA INTERNET
03 Microsoft 7257.14 USA INTERNET
04 Amazon 6756.09 USA INTERNET
05 Tencent 5724.75 CHN INTERNET
06 Facebook 5517.97 USA INTERNET
07 BerkshireHathaway 5363.00 USA INSURANCE
08 Alibaba 5256.00 CHN INTERNET
09 JPMorganChase 4035.98 USA BANK
10 ICBC 3958.31 CHN BANK
EOF

问题 1:
打印 value.list 中包含 Amazon 的行到 Facebook 的行

[root@localhost ~]# sed -n '/Amazon/,/Facebook/p' value.list | column -t
04  Amazon    6756.09  USA  INTERNET
05  Tencent   5724.75  CHN  INTERNET
06  Facebook  5517.97  USA  INTERNET

问题 2:
打印 value.list 的第2,4,6行

[root@localhost ~]# sed -n  '2p;4p;6p' value.list | column -t
NO.  NAME    VALUE(BillionDollars)  Country  Industry
02   Google  8251.05                USA      INTERNET
04   Amazon  6756.09                USA      INTERNET

问题 3:
打印 value.list 的偶数行

[root@localhost ~]# sed -n  '2~2p' value.list | column -t
NO.  NAME      VALUE(BillionDollars)  Country  Industry
02   Google    8251.05                USA      INTERNET
04   Amazon    6756.09                USA      INTERNET
06   Facebook  5517.97                USA      INTERNET
08   Alibaba   5256.00                CHN      INTERNET
10   ICBC      3958.31                CHN      BANK

Tips:
'2~2p' 表示从第二行开始每次增加2,即偶数行。

例 2:

cat >>operator.list<<'EOF'
10086,CHINA-MOBILE,CMB
10010,CHINA-UNICOM,CUC
10001,CHINA-TELECOM,CTC
EOF

问题 1:
operator.list 最后追加以下两行

10050,CHINA-TIETONG,CTT
10060,CHINA-NETCOM,CNT

在知道最后一行的内容时可以用,在书写的时候为便于区分,推荐在 ia 参数前面加一个反斜线 。

[root@localhost ~]# sed '/10001,CHINA-TELECOM,CTC/i\10050,CHINA-TIETONG,CTT\n10060,CHINA-NETCOM,CNT' operator.list 
10086,CHINA-MOBILE,CMB
10010,CHINA-UNICOM,CUC
10050,CHINA-TIETONG,CTT
10060,CHINA-NETCOM,CNT
10001,CHINA-TELECOM,CTC

也可以使用$直接追加到末尾

[root@localhost ~]# sed '$a\10050,CHINA-TIETONG,CTT\n10060,CHINA-NETCOM,CNT' operator.list
10086,CHINA-MOBILE,CMB
10010,CHINA-UNICOM,CUC
10001,CHINA-TELECOM,CTC
10050,CHINA-TIETONG,CTT
10060,CHINA-NETCOM,CNT

问题 2:
operator.list 中包含 10050,CHINA-TIETONG,CTT 的一行前或后增加以下两行

10050,CHINA-TIETONG,CTT
10060,CHINA-NETCOM,CNT

在匹配行前插入

[root@localhost ~]# sed '/10010,CHINA-UNICOM,CUC/i\10050,CHINA-TIETONG,CTT\n10060,CHINA-NETCOM,CNT' operator.list 
10086,CHINA-MOBILE,CMB
10050,CHINA-TIETONG,CTT
10060,CHINA-NETCOM,CNT
10010,CHINA-UNICOM,CUC
10001,CHINA-TELECOM,CTC

在匹配行后插入

[root@localhost ~]# sed '/10010,CHINA-UNICOM,CUC/a\10050,CHINA-TIETONG,CTT\n10060,CHINA-NETCOM,CNT' operator.list 
10086,CHINA-MOBILE,CMB
10010,CHINA-UNICOM,CUC
10050,CHINA-TIETONG,CTT
10060,CHINA-NETCOM,CNT
10001,CHINA-TELECOM,CTC

小贴士:
若记不住a与i的前后顺序,可以简单记为 a == after 后,i == infront 前。

问题 3:
operator.list 第二行后插入 XXXX

[root@localhost ~]# sed 'N;2a\XXXX' operator.list 
10086,CHINA-MOBILE,CMB
10010,CHINA-UNICOM,CUC
XXXX
10001,CHINA-TELECOM,CTC

注意!这里的N表示行号,必须搭配;,后面的数字必须是[偶数],否则不生效(或插入到错误的位置)!

问题 4:
operator.list 中删除包含 1008610010 的行

[root@localhost ~]# sed '/10086/,/10010/d' operator.list 
10001,CHINA-TELECOM,CTC

问题 5:
operator.list 中替换全部数字为空

[root@localhost ~]# sed 's/[0-9]//g' operator.list 
,CHINA-MOBILE,CMB
,CHINA-UNICOM,CUC
,CHINA-TELECOM,CTC

问题 6:
operator.list 中替换字母CF

[root@localhost ~]# sed 's/C/F/g' operator.list 
10086,FHINA-MOBILE,FMB
10010,FHINA-UNIFOM,FUF
10001,FHINA-TELEFOM,FTF
[root@localhost ~]# sed 's/C/F/' operator.list 
10086,FHINA-MOBILE,CMB
10010,FHINA-UNICOM,CUC
10001,FHINA-TELECOM,CTC

问题 7:
过滤出 /etc/passwd 中前五行每列第一个出现的用户名

[root@localhost ~]# sed 's/:.*$//' /etc/passwd | head -n5
root
bin
daemon
adm
lp

也可以使用 cut 命令实现

[root@localhost ~]# cut -d : -f 1 /etc/passwd | head -n5
root
bin
daemon
adm
lp

小结:


二、进阶使用

反向引用

[root@localhost ~]# echo "123456" | sed -r 's/(.*)/<\1>/g'
<123456>
[root@localhost ~]# echo "123456" | sed -r 's/(..)(..)(..)/\1<\2>\3/g'
12<34>56

使用()将引用的内容保护起来,然后再使用\n来引用前面保护的内容。

例 1:取出网卡IP地址

[root@localhost ~]# ip a | sed -rn 's#^.*t (.*)/.*$#\1#gp' | tail -n1
192.168.31.133

例 2:取出文件 /etc/hosts 八进制权限

[root@localhost ~]# stat /etc/hosts | sed -rn '4s#^.*s: \((.*)/-.*$#\1#gp'
0644
[root@localhost ~]# stat /etc/hosts | sed -rn '4s#^.*s: \(([0-9]+)/-.*$#\1#gp'
0644

扩展:正则表达式

基础正则

第一个符号 ^ (以……开头的行)
第二个符号 $ (以……结尾的行)
组合 ^$ (空行)
第三个符号 . (任意一个字符)
第四个符号 \ (转义符,取消效果)
第五个符号 * ([] 前面字符出现0次或者0次以上)
组合 `.
(所有任意符号 包含空行 有贪婪性 连续出现的字符 会匹配到最后一个) 第六个符号[]([abc]相当于一个符号每次匹配一个字符,支持a-z、0-9等) 第七个符号[^](相当于一个符号,每次匹配一个字符)[^abc]表示找出除了a除了b除了c`

扩展正则

第八个符号 + (前一个字符连续出现1次或1次以上)一般与[]配合,取出连续的字符
第九个符号 | (或者)
第十个符号 () (表示一个整体 sed 反向引用、后向引用)
第十一个符号 {} (连续出现)0{n,m} 前一个字符至少连续出现n次,最多连续出现m次
第十二个符号 ? (前面一个字符连续出现0次或者1次)

重点:使用以上扩展正则表达式时需要使用 Sed-r 参数。

举个栗子
去除文件中空行(空行、TAB、空格、混合)

[root@localhost ~]# awk '/^[\t ]*$/' file.txt

命令拼接

需求一:批量添加用户 test01 test02 test03 并设置密码为 123456
解决步骤:
第一步,生成用户名序列

[root@localhost ~]# echo test{01..3} | xargs -n1
test01
test02
test03

第二步,拼接出用户名添加格式

[root@localhost ~]# echo test{01..3} | xargs -n1 | sed 's#.*#useradd &#g'
useradd test01
useradd test02
useradd test03

第三步,拼接出设置密码格式

[root@localhost ~]# echo test{01..3} | xargs -n1 | sed 's#.*#useradd &; echo 123456 | passwd --stdin &#g'
useradd test01; echo 123456 | passwd --stdin test01
useradd test02; echo 123456 | passwd --stdin test02
useradd test03; echo 123456 | passwd --stdin test03

第四步,拼接执行格式

[root@localhost ~]# echo test{01..3} | xargs -n1 | sed 's#.*#useradd &; echo 123456 | passwd --stdin &#g' | bash

需求二:批量修改文件后缀名.txt改成.conf
解决步骤:
第一步,查看文件名

[root@localhost test]# ll
total 0
-rw-r--r--. 1 root root 0 Jul 15 15:26 11.txt
-rw-r--r--. 1 root root 0 Jul 15 15:26 12.txt
-rw-r--r--. 1 root root 0 Jul 15 15:26 13.txt
-rw-r--r--. 1 root root 0 Jul 15 15:26 14.txt
-rw-r--r--. 1 root root 0 Jul 15 15:26 15.txt

第二步,提取出全部的文件名

[root@localhost test]# ls | sed -r 's/(.*)/\1/'
11.txt
12.txt
13.txt
14.txt
15.txt

第三步,拼接出改名格式

[root@localhost test]# ls | sed -r 's/(.*)(txt)/mv & \1conf/'
mv 11.txt 11.conf
mv 12.txt 12.conf
mv 13.txt 13.conf
mv 14.txt 14.conf
mv 15.txt 15.conf

第四步,拼接执行格式

[root@localhost test]# ls | sed -r 's/(.*)(txt)/mv & \1conf/' | bash

参考链接

回复