linux文字修改大師-sed-01-基本介紹


會寫這篇文章,主要是為了自己可以記住龐大的sed功能。
章節的編排主要是依據功能性。
個人使用sed的主要原因是自動化修改設定檔。

目前看過最好的sed和awk中文教學網站大概是底下這個連結
http://wanggen.myweb.hinet.net/ach3/ach3.html

本篇文章主要是依照 GUN 的 sed所撰寫,連結為
https://www.gnu.org/software/sed/manual/sed.html

這邊提供一些線上的正規表示法網頁,可以即時知道自己寫的正規是否有匹配

https://regex101.com/
裡面有很多範例可以直接複製使用

https://regexr.com/
可使用replace功能, 方便驗證 command s 是否正確,裡面還有正規的符號參考



sed簡介

學習sed建議具備正規表示法的基礎,沒有此基礎也可學習。

GUN的sed簡介說到,sed是一種串流型態的文字編輯器

類似一個管道(pipe),特別的是,sed會在管道中間進行文字的取代、刪除、插入等功能。

input =======>output
              filter

sed的input可以是一個或多個檔案、stdin或前一個指令利用 | (pipe)傳到sed



目錄
sed基礎介紹
常用option
address
除了某個條件以外,其它都符合
p指令
s指令
a、i、c、d指令





sed基礎介紹

sed的指令格式為
sed [option] "/address/ , /address/ command argument [flag] " [inputFile...]

從左到右介紹
[option] 第一個option是常見到的以 - 符號開頭的功能,如 -n、-r、-i,可省略
/address/ address有兩個,代表開始行數和結尾行數
              只有一個代表指定的這行,都不輸入代表每行都會執行command
              address可以用正規表示法
command 進行的動作,當匹配address後,會進行怎樣的行為,常見的command有s、a、i、c
argument 給command使用的參數,大部分command後面要接參數
flag 只有 s 指令才會用到
inputFile 指定用來當作input的檔案,會一行一行的讀取

sed因為裡面的指令很複雜,通常會用 "" 或 ' ' 將文字包起來

sed 支援 stdin、stdout、stderr,如:
sed "/error/ p" stdout  列出標準輸出裡有error字樣的那一行文字

常用的 input 方法
有兩種常用的input的方法,使用 | 和從檔案讀取
1. 使用 |
執行ifconfig指令並列出有IP的那一行
ifconfig | sed "/inet/ p"

2. 從檔案讀取
為了自動修改設定檔,這是我最常用的方法
讀取test.txt檔案並將第一行到第五行的apple改成APPLE
sed "1 , 5 s/apple/APPLE/g" test.txt


sed的output
文字經過 sed 的處理後,只會把文字送到 stdout,不會修改到原本的檔案內容。
如果要儲存修改過後的文字需要把結果存回到原檔或存到新的檔案。
可用 sed -w 新檔案.txt "s/aaa/AAA/" 舊檔案.txt 的方式把修改過的文字儲存起來。
或用 sed -i "s/aaa/AAA/" 檔案.txt 的方式直接修改檔案的內容。
也可用以下方法 :
sed "s/aaa/AAA/g" < test.txt > test.txt



sed運作機制
sed以行為單位,一次讀入一行,一行的基準是換行符號(newline或著是\n或ASCII的 0a)
windows的換行是用 \r\n,\r在處理上會有點麻煩。
這時可以用 -b option (sed -b),sed會以 Binary 的方式讀檔案。



sed的暫存區
sed有兩個暫存區,pattern spacehold space
pattern space用來儲存input進來的文字。
hold space是另一個暫存區,用來將pattern space的文字存到此處。
通常hold space是用來處理多行文字所使用(multiple lines)。






依照圖示 :
最一開始,pattern space和hold space是空的。
接著開始循環。
1. sed一次從檔案取出一行,刪除結尾的 \n,再放到 pattern space。
2. 比對address或command s 的pattern,如果比對正確才會執行command。
    如果比對失敗跳到步驟 4。
3. 對 pattern space 的文字執行 command,並將結果存回pattern space。
    如果有多個command,依序執行直到全部command都結束。
4. 將執行完 command 的pattern space送到 stdout,也就是螢幕。
5. 清空 pattern space。
讀取下一行文字,重覆第一步,一直循環到最後一行結束。
整個循環不會清除hold space裡面的文字。


其中也有例外狀況 :
有些 command 或 [option] 會跳過某些步驟。
如 sed -n ,會省略將文字送到螢幕的動作(步驟 4)。這個通常會配合 command p。
command N,不清空 pattern space(步驟 5),直接讀取下一行並放到 pattern space的後面。
command Q或q,結束sed的循環,不管是否還有其它input尚未讀取。




常用option

-r
使用延伸的正規表示法(extended regular expression)。
沒有使用 -r 的正規表示法叫做 Basic regular expression。
主要差異在符號 ?、+、( )、{ }、| 。
沒有用 -r 的情況下,上面的符號沒有特殊意義會被當作文字處理。
如要賦予他原本的正規表示法的功能,需要在前面加上 \ 。如 \?、\( \)。

使用 -r 後,上面的符號會擁有正規表示法賦予的意義,要把這些符號當作文字看待,需要在前面加上 \ 。
範例如下:
cat test.txt
a+b
ab
aab

sed -n "/a+b/ p" test.txt
a+b

使用 -r 後
sed -r -n "/a+b/ p" test.txt
ab
aab

(註解 : + 符號在正規裡面代表,+ 符號前一個文字會出現 1 次或很多次,在這裡就是 a 文字會出現一次或多個連續的 a)
主要用在是否會大量使用正規表示法的特殊符號,使用 -r 功能會大幅減少 \ 符號的使用,增加可看度。


-n
不將執行完的結果顯示在螢幕上(stdout)。
通常會配合 command p 或 command s 的flag p。
如 :
cat test.txt
hello
world

sed "s/hello/HELLO/" test.txt
HELLO
world

使用 -n 後
sed -n "s/hello/HELLO/" test.txt

加上 p flag
sed -n "s/hello/HELLO/p" test.txt
HELLO

底下方式,可用來檢察 /address/ 的正規表示法是否有錯
sed -r -n "/hel+o/ p" test.txt
hello

-n 的功能我常搭配 -p ,用來檢查 /address/ 的正規表示法是否有錯。


-i[suffix]
直接修改檔案的內容,而不是將資料輸出stdout,其功能類似 > file.txt。
-i 和 -n 通常不會一起使用,因為沒有執行 command 的行數會被刪除。
下面以範例解是。

如 :
cat test.txt
Line1
Line2
Line3

sed -i "s/Line3/Line4/" test.txt

cat test.txt
Line1
Line2
Line4


如果加上 -n
sed -in "s/Line3/Line4/" test.txt

cat test.txt
Line4

因為 -i 等同於 sed "指令" < test.txt > test.txt,如果使用 -n 原始文件會只剩有修改過的文字,其他沒有修改過的文字會不見。


[suffix]
-i 後面可以加上文字,把原本的舊檔案改名成 "舊檔案名稱[suffix]"。
-i 後面不用空格。
-i 後面接的任何文字,都會被當作suffix處理,除了 " " 或 ' '以外。
所以要用其他的[option],請加在 i 的前面。
範例 :
sed -ir "s/aaa/AAA/" test.txt
最後會變成
test.txt  (新檔案)
test.txtr (舊檔案)


--follow-symlinks
這個功能只有在使用 -i 才會有用。
如果要修改的檔案名稱是捷徑(使用 ln -s 建立),會跟著捷徑去修改原頭的檔案。
sed --follow-symlinks -i "s/aaa/AAA/g" test.ln


-f script.file
使用sed的script檔。
如果覺得要一行一行使用CLI去執行sed很麻煩,可以把全部寫在一個檔案內,重複使用。
還可增加可讀性。
注意 : script的第一行如果要用註解,不能用 #n 開頭,會被當作 sed -n。
範例 :
建立一個script,先抓取位置為Line1文字到Line3文字之間,取代Line2變成LINE2,並再一次比對address /Line1/,如果比對成功刪掉此行。
cat script.txt
/Line1/ , /Line3/ {
s/Line2/LINE2/
/Line1/ d
p
}

cat test.txt
Line1
Line2
Line3

sed -f script.txt test.txt
LINE2
Line3




address
定義command會執行的行數。
( s command 還需額外匹配自己的正規表示法)

address可以用數字或正規表示法。
使用數字代表第幾行,使用正規表示法代表要匹配到才會執行。

大致的書寫方法有三種 :
不寫 address
address
address1 , address2
$


不寫address
sed不指定 address 時,代表每一行都會執行 command。

範例:
每一行都插入 GOOD 文字
seq 3 | sed " a GOOD"
1
GOOD
2
GOOD
3
GOOD


address
只輸入一個address
代表只有這一行會執行 command。
如果是用數字,只有一行會執行。
如果是用正規表示法,匹配到的行數都會執行。
seq 3 | sed "2 a GOOD"
1
2
GOOD
3


address1 , address2
輸入兩個address,並且兩個之間使用 , 符號隔開。
代表一個範圍,範圍內的行數都會執行 command 。
可以數字和正規表示法混搭。

範例 :
在第一行和第二行後面插入 GOOD 文字。
seq 3 | sed "1 , 2 a GOOD"
1
GOOD
2
GOOD
3


$
這個符號在 addres 的位置時,代表最後一行
如 :
從第一行到最後一行,每行都插入 GOOD 文字。
seq 3 | sed "1 , $ a GOOD"
1
GOOD
2
GOOD
3
GOOD

(這個等同於不使用 address,sed " a GOOD" )



address有很多種使用的方法,大致分成:
純數字
純/正規/
數字 , /正規/ 混搭


純數字
代表指定行數,如果使用兩個address,代表著這兩個行數之間
範例:
只顯示第二行的文字
seq 4 | sed -n "2 p"
2

顯示第二行到第四行的文字
seq 4 | sed -n "2 , 4 p"
2
3
4


純/正規/
正規表示法有區分大小寫。
如果只有一個address,代表只有比對成功的行數才會執行。
如果有兩個address,會先比對第一個address,比對成功後,會從這行開始,再比對第二個address,而這兩個中間的行數,每行都會執行 command。
cat test.txt
aaa
bbb
acc
ddd

只要有 a 的都顯示出來
sed -n "/a/ p" test.txt
aaa
acc

從有 b 文字的行數開始,一直顯示到有 d 文字的行數
sed -n "/b/ , /d/ p" test.txt
bbb
acc
ddd



數字 , /正規/ 混搭
數字先,後/正規/ : 從指定的行數開始才會用第二個address(正規)尋找匹配的行數。
這兩個中間的行數,每行都會執行 command。

從第二行開始,一直顯示到有 d 文字的行數
sed -n "2 , /d/ p" test.txt
bbb
acc
ddd



address的特殊表示方法
\%正規%
/正規/flag
數字~數字
/正規/or數字 , ~數字
/正規/or數字 , +數字
0,/正規/


\%正規%
如果使用的正規表示法內,有很多個 / 符號是需要被匹配的,可以用替換方式把原本包起正規的 / / 符號改成自己指定的符號。
使用方法是  \符號  正規表示法  符號。
範例 :
只要有 /var/log 的行數都刪除
sed "\%/var/log% d" test.txt
如果沒有替換符號,原本的寫法會是
sed "/\/var/\log/ d" test.txt
閱讀性比較差

而符號可以自訂。
如 :
sed "\@/var/log@ d"test.txt



/正規/flag
address 如果使用正規,有兩個 flag可以使用。
I
大寫的 I ,因為小寫的 i 被用在 command。
代表 不區分大小寫 。
sed "/B/I , /D/I p"
bbb
acc
ddd


M
多行匹配。



數字~數字
這個代表的是,行數~step。
從指定的行數開始,每次都會跳固定行數。
seq 10 | sed -n "3~2 p"
3
5
7
9

如果使用 ~0 ,其結果跟沒有使用 ~0 一樣。
seq 10 | sed -n "3~0 p"
3


/正規/or數字 , ~數字
到 ~數字 的倍數行數後停止。
seq 10 | sed -n "/2/ , ~4 p"
2
3
4

seq 10 | sed -n "/5/ , ~4 p"
5
6
7
8


/正規/or數字 , +數字
從指定行數開始,繼續往下 +數字 個行數。
seq 10 | sed -n "/2/ , +4 p"
2
3
4
5
6


0,/正規/
這個功能很特別。
如果要匹配的東西可能不會出現,但是一出現會在第一行和第二行,但是只想要在第一行執行 command。
底下的範例會說明使用 0 和使用 1 的差異。
seq 3 | sed -n "0 , /[0-9]/ p"
1

seq 3 | sed -n "1 , /[0-9]/ p"
1
2

因為address使用數字和正規表示法的混搭方法時,第一個address的數字不會被列為第二個address的比對範圍內,因此第一個address的行數一定會被執行,然後從下一行開始才進行正規的匹配。



address的矛盾
有時在設計address時,會出現只比對到開始或只比對到結束,或第一個數字比第二個數字大,這些特殊狀況發生時,sed 會如何運作。
這種情況都是使用兩個address才會發生,底下列出可能發生的範例。
底下的範例都用:
cat test.txt
Line1
Line2
Line3
Line4
Line5



數字 1 , 數字 2
數字 1 > 數字 2 的狀況,等同於 數字 1
sed -n "3 , 1 p" test.txt
Line3


數字 , /正規/
從 數字 行數以後都無法匹配到正規,等同於 數字 , $
sed -n "3 , /AAA/ p" test.txt
Line3
Line4
Line5


/正規/ , 數字
匹配到 /正規/ 的行數比 數字 大,等同於 /正規/
sed -n "/Line3/ , 1 p" test.txt
Line3

sed -n "/Line[3-4]/ , 1 p" test.txt
Line3
Line4


/正規1/ , /正規2/
只匹配到/正規1/,等同於 /正規1/ , $
sed -n "/Line3/ , /AAA/ p" test.txt
Line3
Line4
Line5


只匹配到/正規2/,等同於沒有作用
sed -n "/AAA/ , /Line2/ p" test.txt
不會顯示任何東西



除了某個條件以外,其它都符合
! 符號可以反轉 command 要執行的行數。
有時候會有除了某個條件以外的需求,例如除了第三行以外,或著除了有apply的字樣以外,當有這種需求的時候可以使用 ! 符號。

範例:
除了第三行,其它都顯示
seq 5 | sed -n '3 !p'
1
2
4
5


除了 DBSTART 到 DBEND之間,其它行只要有 host 就改成 127.0.0.1
sed -i '/DBSTART/ , /DBEND/ !s/host/127.0.0.1/g' config.conf


除了127開頭的以外,其它都顯示出來
echo -e "192.168.1.1\n172.16.1.1\n127.0.0.1" | sed -n '/^127/ !p'
192.168.1.1
172.16.1.1



p , = 指令
p 指令
印出文字
通常配合 -n 一起使用。
範例:
檢驗正規表示法是否有正確匹配到電話號碼
cat phone.txt
011234567
(01)1234567
(01)123-4567

sed -rn "/\(\d{2}\)\d{3}-\d{4}/ p" phone.txt
(01)123-4567

sed -rn "/\(\d{2}\)\d{3}-?\d{4}/ p" phone.txt
(01)1234567
(01)123-4567

sed -rn "/\(?\d{2}\)?\d{3}-?\d{4}/ p" phone.txt
011234567
(01)1234567
(01)123-4567


也可做為 grep 使用
列出主機 IP
ifconfig | sed -n "/inet/ p"


= 指令
印出行數
cat test.txt
aaa
bbb
ccc

sed -n "/a/ =" test.txt
1

sed -n "/a/ , /c/ =" test.txt
1
2
3


s指令
substitute,取代。
s指令的格式為 :
s/pattern/replace/flag

/pattern/ 是比對的文字,如果比對成功,/pattern/內的文字會被/replace/取代
flag,有w、g、p、e、iI、mM、數字、g+數字

範例 :
只要是 an 就改成 AN 。這裡 an 的前後都有一個空格,這個空格也會被當作是比對的字元
cat test.xt
The girl, anna, had an apple.

sed "s/ an / AN /g" test.txt
The girl, anna, had AN apple.

上面的 anna ,因為 an後面接的不是空格,因此不會被認定為比對成功


flag介紹
沒有使用 g 或數字
只取代第一次比對到的文字。
範例 :
cat test.txt
anananan
sed "s/an/AN/" test.txt
ANananan


g
全部比對到的文字都會進行取代。最常用的 flag。
範例 :
sed "s/an/AN/" test.txt
ANANANAN

數字
剛好比對到 /pattern/ 指定的次數才會取代裡面的文字,在比對到指定次數之前和之後都不會進行取代。
範例 :
sed "s/an/AN/2" test.txt
anANanan

g+數字
第幾次比對到後,才開始進行取代
範例 :
sed s/an/AN/g2" test.txt
ananANAN

p
當取代成功後,把取代成功的文字印出。通常配合 -n 一起用。

i或I
/pattern/ 的比對變成大小寫相同(case-insensitive)。原本的比對會區分大小寫。

m或M
變成多行匹配模式(multi-line)。

e
將取代成功的文字送到shell,把文字當作指令執行,並把結果回存到pattern space.
sed -n "s/Line1/ifconfig/ep" test.txt

eth0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether b8:27:eb:8f:d5:ca  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1  (Local Loopback)
        RX packets 701  bytes 66724 (65.1 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 701  bytes 66724 (65.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


w filename
將取代成功的文字存到指定的檔案內,支援/dev/stdout和/dev/stderr。
注意 : w 後面不能再執行其它 command,sed會把 w 後面的全部文字(一直到 " 或 ' 符號)當作檔名,如 sed "s/abc/ABC/w out.txt ; p" test.txt,會存到檔名叫 "out.txt ; p" 這樣的檔案,可以用多個 -e 隔開,如 sed -e "s/abc/ABC/w out.txt" -e "p" test.txt。


s 指令的特殊符號
s 指令可以在 /replace/ 的部分,使用特殊符號。

\1-9
這個功能也可以用在 /pattern/ 上。
反斜線之後加上 1 - 9 的數字。代表正規表示法中的 $1 到 $9。
範例 :
cat test.txt
NAME:jack

sed "s/NAME:(.*)/HELLO \1/" test.txt
HELLO jack


&
等於 /pattern/ 的文字。
範例 :
cat test.txt
Line is a lie.

sed "s/ne/&AA/" test.txt
LineAA is a lie.

sed "s/ne/AA&/" test.txt
LiAAne is a lie.


\u
這個符號的下一個英文字改成大寫。
\U
從這個符號的下一個英文開始,都改成大寫,直到碰到\L、\E。
\l
這個符號的下一個英文字改成小寫。
\L
從這個符號的下一個英文開始,都改成小寫,直到碰到\U、\E。
\E
停止\U、\L的功能。
範例:
cat test.txt
NAME:jack

sed "s/(NAME:)(.*)/\L\1\U\2/" test.txt
name:JACK




a、i、c、d指令
這四個指令常會配合 /address/ 使用,功能分別是:
a 在下一行插入文字。
i 在上一行插入文字,會把原本的文字往下擠一行。
c 把這一整行取代。
d 刪掉這一行。

a
a\
在指定行數的下一行插入文字。
因為 a 指令會把放在 a 後面的空白都忽略,因此如果要有縮排或著是想要讓觀看更方便而使用換行方式書寫時,可以使用 a\ 。
如果要插入的文字很多行,可以用 \n 代表換行。
也可在插入的文字後面使用一個 \ 可以換行書寫(這個不等於換行符號)。
可使用 /address/,/address/ 的方式,在指定的行數內,每行都插入文字。
如果沒有使用 /address/ 指定行數,會每行都插入文字。

i
i\
功能同 a,差在於會在指定行數的上一行插入文字。
i\ 功能同上。

c
c\
把指定行數的文字整行刪除,替換成給定的文字。
如果 c 指令後面沒有給予文字,功能類似 d 指令。
c\ 功能同上。
可使用 /address/,/address/ 的方式用一行文字取代多行文字。
如果沒有使用 /address/ 指定行數,全部內容都會被取代。

d
把指定行數刪除。
可使用 /address/,/address/ 的方式刪除多行。
如果沒有使用 /address/ 指定行數,會把全部內容清空。


範例:
cat test.txt
Line1
Line2
Line3
Line4

sed "/Line2/ a AAAA" test.txt
Line1
Line2
AAAA
Line3
Line4

sed "/Line2/ i AAAA" test.txt
Line1
AAAA
Line2
Line3
Line4

sed "/Line2/ c AAAA" test.txt
Line1
AAAA
Line3
Line4

sed "/Line2/ d" test.txt
Line1
Line3
Line4


sed "/Line2/,/Line3/ a AAAA" test.txt
Line1
Line2
AAAA
Line3
AAAA
Line4

sed "/Line2/,/Line3/ i AAAA" test.txt
Line1
AAAA
Line2
AAAA
Line3
Line4

sed "/Line2/,/Line3/ c AAAA" test.txt
Line1
AAAA
Line4

sed "/Line2/,/Line3/ d" test.txt
Line1
Line4


sed " a AAAA" test.txt
Line1
AAAA
Line2
AAAA
Line3
AAAA
Line4
AAAA

sed " i AAAA" test.txt
AAAA
Line1
AAAA
Line2
AAAA
Line3
AAAA
Line4

sed " c AAAA" test.txt
AAAA

sed "/ d" test.txt



使用 \ 符號的差異
sed "/1/ a           AAA" test.txt

Line1
AAA



sed "/1/ a\    AAA" test.txt

Line1
    AAA



sed "/1/ a\
    AAA" test.txt

Line1
    AAA



如果要插入多行,並且方便閱讀
sed "/1/ a\
    AAA\n\
    BBB\n\
    CCC\
" test.txt

Line1
    AAA
    BBB
    CCC

其中 \n\ 裡面其實是兩個功能,分別是 \n 和 \。
\n 代表輸出的換行符號。
\   代表可換行書寫。

留言

發佈留言

此網誌的熱門文章

WPA_supplicant的設定方式

DOS指令 -- SET和變數

Nginx server 和 location 優先順序