天天看點

sed指令總結

張賀,多年網際網路工作經驗,擔任過網絡工程師、系統內建工程師、LINUX系統運維工程師

目錄

  • 1、概述
  • 2、查
    • 1、列印整行(一或多)
    • 2、正則列印包含關鍵字的行
  • 2、增
  • 3、删
  • 4、替換
  • 5、後向引用
  • 6、結合
  • 7、練習
  • 8、進階

sed的作用我們就記得兩條就可以了:替換和增删改查,也就是說今後當我們想對檔案進行一些文字的替換和增删改查時就要想起sed。

sed的文法分成三部分,我們來舉一個例子:

//sed <選項-n> <對誰操作,3代表第3行> <幹啥p,p代表列印>  <要操作的檔案>
sed -n 3p /etc/passwd   
      

sed的執行過程:

将檔案"吸入"記憶體,然後在記憶體裡面處理,處理好之後将空間内的内容傾倒到螢幕。

sed常用的選項其實就三個最為常用:

-n:僅顯示處理的行

-r:使其支援擴充的正規表達式

-i:sed預設不改變檔案的内容,使用-i會改變檔案的内容,慎用!

-e:-e選項允許在同一行裡執行多條指令(不好用)

1、顯示檔案的哪一行,或是哪幾行,要求我們提示知道要顯示的東西哪幾一行,如果不知道想要的行在檔案是第幾行,那麼可以先用cat -n或是less -N進行檢視确認,然後再用sed列印。

//通過這個例子體會`-n`這個選項的作用
[root@zabbix3 tmp]# cat test.txt 
zhanghe
zhangjia
zhangwei
[root@zabbix3 tmp]# sed 2p test.txt
zhanghe
zhangjia
zhangjia
zhangwei
[root@zabbix3 tmp]# sed -n 2p test.txt
zhangjia
      
[root@zabbix3 tmp]# cat test.txt 
zhanghe
zhangjia
zhangwei
//僅列印最後一行
[root@zabbix3 tmp]# sed -n '2p' test.txt
zhangjia
//列印最後一行
[root@zabbix3 tmp]# sed -n '$p' test.txt
zhangwei
//下面這兩個例子效果是一樣的
[root@zabbix3 tmp]# sed -n '2,3p' test.txt
zhangjia
zhangwei
[root@zabbix3 tmp]# sed -n '2,+1p' test.txt
zhangjia
zhangwei
      

2、我們想要顯示出現某個關鍵字的行,比如找出/etc/passwd當中開頭是zhanghe關鍵字的行,我們就得使用正規表達式進行比對,在sed當中一旦想要使用正規表達式的話就要使用//這兩個符号,在這兩上符号内部寫正規表達式。

//其實不用sed,通過grep實作這個需求更簡單,不是嗎?如下所示:
[root@zabbix3 ~]# grep "^zhanghe" /etc/passwd
zhanghe:x:1000:1000::/home/zhanghe:/bin/bash

//下面是這是通過sed實作的,看着還麻煩一點
[root@zabbix3 ~]# sed -n '/^zhanghe/p' /etc/passwd
zhanghe:x:1000:1000::/home/zhanghe:/bin/bash
      
// 顯示結尾是/bin/bash的行
[root@zabbix3 ~]# grep '/bin/bash$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
zhanghe:x:1000:1000::/home/zhanghe:/bin/bash

//下面是這是通過sed實作的
[root@zabbix3 ~]# sed -n '/\/bin\/bash$/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
zhanghe:x:1000:1000::/home/zhanghe:/bin/bash
      

顯示開頭是root或者zhanghe的行

//“|”符号屬于擴充的正則,是以sed加r,grep加e
[root@zabbix3 ~]# egrep '^(root|zhanghe)' /etc/passwd
root:x:0:0:root:/root:/bin/bash
zhanghe:x:1000:1000::/home/zhanghe:/bin/bash
[root@zabbix3 ~]# sed -rn '/^(root|zhanghe)/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
zhanghe:x:1000:1000::/home/zhanghe:/bin/bash
      

列印出開頭是root行一直到結尾nologin的中間的行,我們還是可以使用正規表達式

[root@localhost ~]# sed -n '/^root/,/nologin$/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
      

其實用正規表達式比對範圍非常适合我們用來檢視日志,假如說我們檢視一下某個時間點到另一個時間點之間發生了什麼事情?如下所示:

[root@localhost ~]# sed -n '/Oct 21 15:42:48/,/Oct 21 16:09:26/p' /var/log/secure
      
增常常于查連用,而查無非是根據行号和正規表達式查詢,一定要體會到這個聯系!才會把sed的查和增聯系起來

我們在文本後面增加東西有幾種方法呢?vim、nano、>>(echo 、cat)除了這些方法之後,在文本裡面增加内容也隻有sed了。

通過sed在文本裡面增加内容實際上非常的簡單,主要就用到三個選項:a/i/c。

a:apped在比對到的行下面加

i:insert在比對到的行上面加

r:在行的後面加内容,加的内容必須來自于檔案

c:把這一行内容替換成你想要的内容 #看着是不是有點熟悉,grep也有類似的選項

y:作用字元替換,将比對的内容做替換

w:将比對到的行輸出到另一個檔案

//在文本的第3行下面添加兩行内容
[root@localhost ~]# sed '3azhanghe\nzhanghe' /etc/passwd  zhanghe,zhanghe

//在開頭是root的行下面添加兩行内容zhanghe,zhanghe
[root@localhost ~]# sed '/^root/azhanghe\nzhanghe' /etc/passwd 

//把第一行整體替換成zhanghe,這是替換的一整行
[root@localhost ~]# sed '1czhanghe' /etc/passwd | head -2     
zhanghe
bin:x:1:1:bin:/bin:/sbin/nologin

//在/etc/passwd檔案裡面比對到以root開頭的行,然後這一行的下一行添加/tmp/text.txt裡面的内容
sed '/^root/r /tmp/text.txt' /etc/passwd

//把text.txt檔案裡面包括my的行放置到text2.txt裡面,注意這裡面的順序
sed -n '/my/w test2.txt' test.txt  

将前10行當中的所有小寫的s轉換成大寫的S和将全文所有小寫的s轉成大寫的S
sed '1,10y/s/S/' /etc/passwd
      

删除最簡單的了,就是使用一個局部指令d就可以了,如下所示:

//删除第一行
sed '1d' /tmp/passwd 

//删除1、2、3行
sed '1,3d' /etc/passwd   

//删除開頭的root的行一直到結尾是nologin的行
sed '/^root/,/nologin$/d' /tmp/passwd  
      
//删除開頭是#号的行
sed '/^#/d' /etc/nginx/nginx.conf

//删除真空行
sed '/^$/d' /etc/nginx/nginx.conf

//删除帶空格的假空行(平時記住這個即可)
sed '/^[[:space:]]*$/d' /etc/nginx/nginx.conf
      

在sed的替換功能這裡面我們要對替換做一個總結,文本的替換有很多方法,我們來總結一下,目前講的sed的通用替換方式即:s@@@這種方式, 這種方式是通用的查找到什麼就能替換什麼,靈活強大,某字元的替換、大小寫的替換皆可做,但是整行的替換通過s@@@不太好做,需要用到二級指令c

//字元替換(詞語、單個字元)
 sed 's@root@R00T@g' /etc/passwd
 sed '1,10y/s/S/' /etc/passwd
 tr 's' 'S' < /etc/passwd

//大小寫的轉換,u和l代表和upper和lower
sed 's@[a-z]@\u&@g'  file
sed 's@[A-Z]@\l&@g'  file
tr '[a-z]' '[A-Z]' < /etc/passwd
tr '[[:lower:]]' '[[:upper:]]' < /etc/passwd

//整行替換,将第二行無論什麼内容都替換成888
sed 2c888 /etc/passwd
      

替換是sed最重要的功能,也比較簡單,我們隻需要記住sed替換的标準格式,即:

//s是`sub`的意思,g是`global`就是全局的意思,整體意思就是全局替換。
sed 's@@@g'
      
[root@localhost tmp]# cat test.txt
zhanghe
zhangmin
zhangjia
zhanghe
zhanghezhanghe

// 不加g,隻會替換第一行
[root@localhost tmp]# sed 's@zhanghe@hello@' test.txt 
hello
zhangmin
zhangjia
hello
hellozhanghe

//加上g就是全局替換
[root@localhost tmp]# sed 's@zhanghe@hello@g' test.txt  
hello
zhangmin
zhangjia
hello
hellohello
      

其實g所在的位置指代的是哪幾代,g是指全部嘛,如果寫一個3那就是第三列,也就是說我們可以指定替換哪一列當中的字元串,如下所示,我們替換第三列當中的zhanghe為hello:

[root@localhost tmp]# cat test.txt
zhanghe
zhangmin
zhangjia
zhanghe 
zhanghezhanghe
zhanghe

//#隻有第三列變化了,第一列和第二列的zhanghe都沒有被替換
[root@localhost tmp]# sed 's@zhanghe@hello@3' test.txt  
zhanghe
zhangmin
zhangjia
zhanghe 
zhanghezhanghe hello     #注意,這是按照詞語進行替換的,我們上面講的-c選項是按行進行替換的。
      

在替換當中,隻有第一個條件可以使用模式,第二個不可以

[root@zhanghe ~]# cat zh.txt
i like
on,my,love
[root@zhanghe ~]# sed 's#\(l..e\)#\1r#' zh.txt   #把l..e替換成l..er
i liker
on,my,lover
[root@zhanghe ~]# sed 's#l\(..e\)#L\1#' zh.txt   #僅把l..e的l替換成大寫
i Like
on,my,Love
      
//利用sed指令把history開始的空白字元給删除了
history | sed 's@^[[:space:]]*@@'
history | sed 's@^[[:space:]]\+@@g'
history|sed 's#^[[:space:]]\{3\}##g'
      

所謂的後向引用就是将想要引用的東西用括号包起來,如果再用到的話就可以直接調用了,就是這麼簡單。

[root@localhost tmp]# echo 123456 | sed -r 's@(.*)@\1@g'    #.*就代表所有
123456
[root@localhost tmp]# echo 123456 | sed -r 's@1234(.*)@\1@g'  #這個所有指代的就是5和6
56
      
//取IP
[root@zabbix3 ~]# ifconfig eth0 | sed -n 2p
        inet 192.168.80.199  netmask 255.255.255.0  broadcast 192.168.80.255
[root@zabbix3 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*et (.*) net.*@\1@'
192.168.80.199 
      

同時執行多條sed語句,-e選項允許在同一行裡執行多條指令

//先将第2行到最後一行給删除了,隻留下第一行,然後将第一行的ROOT替換成TTTTT
sed -e '2,$d' -e 's@ROOT@TTTTT@' /etc/passwd
      

删除/etc/grub.conf檔案中行首的空白字元(提示:替換)

[root@zhanghe ~]# sed 's@^[[:space:]]@@' /etc/grub.conf
      

替換/etc/inittab檔案當中的id:3:initdefault:一行當中的數字為5(提示:後向引用)

[root@A ~]# sed "s%^id:[0-9]:initdefault:$%id:5:initdefault:%" /etc/inittab
[root@China ~]# sed "s@\(id:\)[0-9]\(:initdefault:\)@\15\2@g" /etc/inittab
sed "s@\(id:\)[[:digit:]]\(:initdefault:\)@\16\2@" /etc/inittab
sed -r -i  "s@(id:)[[:digit:]](:initdefault:)@\16\2@" /etc/inittab
      

删除/etc/inittab檔案當中的空白行(提示:删除)

[root@China ~]# sed "/^[[:space:]]*$/d" /tmp/grub.conf
      

删除/etc/inittab檔案當中以#開頭的行(提示:删除)

[root@zhanghe ~]# sed  "/^#/d" /etc/inittab
      

删除某檔案中開頭的#及後面的空白字元的行,但要求#号後面必須有空白字元(提示:删除)

[root@zhanghe ~]# sed "/^#[[:space:]]/d" test.sh
      

删除某檔案中以空白字元後面跟#号的行中開頭的空白字元及#(提示:删除)

[root@zhanghe ~]# sed "s@^[[:space:]]\+#@@" test.sh
      

取出一個檔案路徑的目錄名稱(後向引用)

[root@China ~]# echo "/etc/sysconfig" | sed 's@[^/]\+$@@'
/etc/
[root@China ~]# echo "/etc/sysconfig/" | sed 's@[^/]\+$@@'
/etc/sysconfig/
[root@China ~]# echo "/etc/rc.d" | sed -r "s@^(/.*/)[^/]+/?@\1@g"
/etc/
[root@China ~]# echo "/etc/rc.d" | sed -r "s@[^/]+/?\$@@g"
/etc/
      

解析:第一回是線上開頭的字元至少出現一次,并且還要在詞尾給删了,删除之後可不就剩下斜線開頭的目錄了嘛,但是,如果目錄是一個絕對路徑呢?就像

echo /zhang/he/ | sed "s@\(\/[[:alnum:]]\+\/\)\([[:alnum:]]\+\/\?\)@\1@"
/zhang/
echo /zhang/he/ | sed "s@\(\/[[:alnum:]]\+\/\)\([[:alnum:]]\+\/\?\)@\2@"
he/
      

取出一個目錄的基名和目錄名()

[root@China ~]# basename /etc/sysconfig
sysconfig
[root@China ~]# dirname /etc/sysconfig
/etc
      

把/etc/fstab當中空行和開頭是空格的、開頭是#号都删除掉(提示:删除)

//把/etc/fstab當中空行和開頭是空格的、開頭是#号都删除掉
sed 's@^#@@' /etc/fstab | sed '/^[[:space:]]*$/d' | sed 's@^[[:space:]]@@'

//把/etc/fstab當中空行和開頭是空格的、開頭是#号的行都删除掉,注意,上面是删除字,實質是替換,這裡是删行
sed '/^#/d' test.txt | sed '/^[[:space:]]/d' | sed '/^[[:space:]]*$/d'
      

列印奇數或偶數行

sed -n 'p;n' <file>   #列印奇數行
sed -n 'n;p' <file>   #列印偶數行
      

列印完前三行後,退出sed

[root@zhanghe ~]# sed '3q' /etc/passwd
ROOT:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
      

将檔案當中所有的字母轉成大寫

最好的辦法不是使用sed而是使用tr,如果tr指令的話是這樣:

tr 'a-z' 'A-Z' < file
      

将檔案當中前10的abcde轉成為大寫

sed '1,10y/abcde/ABCDE/' /etc/passwd
      

将前10行當中的所有小寫的s轉換成大寫的S和将全文所有小寫的s轉成大寫的S

sed '1,10y/s/S/' /etc/passwd
sed 's@s@S@g' /etc/passwd 
      
是否真正了解了sed是一個行編輯器?

sed是一個行編輯器,行編輯器的意思是隻能一行行的處理,比如你可以删除任意行,但不能删除某一行當中的某一個字段,如下所示:

//删除第二行
[root@n9 ~]# cat -n /etc/issue
     1	\S
     2	Kernel \r on an \m
     3	
     
[root@n9 ~]# cat -n /etc/issue | sed 2d
     1	\S
     3	
     
//删除第二行當中on單詞,是無法删除的,無法删除的原因并不是沒有比對上,而是做不到,
[root@n9 ~]# cat -n /etc/issue | sed -n 2p | sed '/on/d'
     2	Kernel \r on an \m
     
//通過grep我們可以确定最後一個sed是一定是比對到了on,但sed删除功能的細粒度隻是行而已,做不到僅删除字元串。
      
sed指令總結

在使用替換s@A@B@格式的時候,第一次比對,也就是A處的比對一定要把一整行全都比對上,不能僅僅比對一行當中的某個或某些字段,這麼說有些抽象,我們用例子來說明:

[root@n9 ~]# ifconfig eth0 | sed -n 2p
        inet 192.168.80.59  netmask 255.255.255.0  broadcast 192.168.80.255
[root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask@\1@'
192.168.80.59  255.255.255.0  broadcast 192.168.80.255
[root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask.*@\1@'
192.168.80.59 
      
[root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask@\1@'
192.168.80.59  255.255.255.0  broadcast 192.168.80.255
      
[root@n9 ~]# ifconfig eth0 | egrep -o '^.*inet (.*) netmask'
        inet 192.168.80.59  netmask
      
//這條指令到底錯了哪裡?
[root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask@\1@'
192.168.80.59  255.255.255.0  broadcast 192.168.80.255
      
[root@n9 ~]# ifconfig eth0 | sed -n 2p | sed -r 's@^.*inet (.*) netmask.*@\1@'
192.168.80.59
      
ip addr show eth0 | sed -n 3p
    inet 192.168.80.59/24 brd 192.168.80.255 scope global noprefixroute eth0
    
ip addr show eth0 | sed -n 3p | sed -r 's@[[:space:]]+inet (.*) brd@\1@'
192.168.80.59/24 192.168.80.255 scope global noprefixroute eth0

ip addr show eth0 | sed -n 3p | sed -r 's@[[:space:]]+inet (.*) brd.*@\1@'
192.168.80.59/24

ip addr show eth0 | sed -n 3p | sed -r 's@[[:space:]]+inet (.*) (\bbrd\b.*eth0$)@\1@'
192.168.80.59/24
      

繼續閱讀