張賀,多年互聯網工做經驗,擔任過網絡工程師、系統集成工程師、LINUX系統運維工程師
nginx
一、概述
sed的做用咱們就記得兩條就能夠了:替換和增刪改查,也就是說從此當咱們想對文件進行一些文字的替換和增刪改查時就要想起sed。git
sed的語法分紅三部分,咱們來舉一個例子:正則表達式
//sed <選項-n> <對誰操做,3表明第3行> <幹啥p,p表明打印> <要操做的文件> sed -n 3p /etc/passwd
sed的執行過程:vim
將文件"吸入"內存,而後在內存裏面處理,處理好以後將空間內的內容傾倒到屏幕。bash
sed經常使用的選項其實就三個最爲經常使用:網絡
二、查-n:僅顯示處理的行app
-r:使其支持擴展的正則表達式less
-i:sed默認不改變文件的內容,使用-i會改變文件的內容,慎用!運維
-e:-e選項容許在同一行裏執行多條命令(很差用)編輯器
一、顯示文件的哪一行,或是哪幾行,要求咱們提示知道要顯示的東西哪幾一行,若是不知道想要的行在文件是第幾行,那麼能夠先用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
二、咱們想要顯示出現某個關鍵字的行,好比找出/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 //刪除一、二、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刪除功能的細粒度只是行而已,作不到僅刪除字符串。
在使用替換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
上面這個爲何會出錯呢?不是由於擴展的正則表達式寫錯了,那我怎麼確定我沒有寫錯呢?經過egrep就能夠肯定,我把sed使用的擴展正則表達式^.*inet (.*) netmask放到egrep裏面,就能看見到底匹配到了哪些內容,以下所示,內容並無匹配錯:
[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
如上所示,在s@^.*inet (.*) netmask@\1@正則表達式當中,(.*)匹配的內容並不只僅匹配的是inet後面的內容和netmask前面的內容,(.*)的真正的含義是匹配除了^.*inet匹配到的,和除了netmask這個單詞以外內容,結果就是就把192.168.80.59 255.255.255.0 broadcast 192.168.80.255給匹配出來了。
解決辦法咱們上面說過了,只要在第一次匹配的時候把要處理的行匹配完整,就不會有這種狀況發生,也就是在netmask後面加上.*,這樣就把一行匹配完整了,下面舉幾個例子:
[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