Linux
中可以使用強大的編輯器如 vim
和 emacs
來編輯文本文件。但有時候,我們只是需要自動的處理文本文件,而不是這些強大的交互式文本編輯器。這種時候可以使用 Linux
的 sed
和 gawk
。它們能夠輕松的實現自動格式化、插入、修改或者刪除文本元素。
sed
編輯器
sed
編輯器被稱為流編輯器(stream editor), 它使用預先提供的一組規則來編輯數據流。規則就是提供給 sed
的命令,這些命令要么從命令行輸入,要么存儲在一個命令行文件中。根據所提供的命令,sed
執行如下操作:
- 一次從輸入中讀取一行數據
- 根據提供的命令匹配數據
- 按照命令修改流中的數據
- 將新的數據輸出到
STDOUT
在流編輯器將所有命令與一行數據匹配完畢后,就會重復這個流程匹配下一行數據。在處理完所有的流數據后,它就會終止。由于命令是逐行執行的,所以只需要對數據流進行一遍處理就可以完成編輯操作,這比交互式編輯器快的多,而且是完全自動化的操作數據。
sed
的命令格式如下:
sed options script file
選項可以修改 sed
命令的行為,可以使用的選項如下表:
options | desc |
---|---|
-e script | 將 script 中的命名添加到已有的命令中 |
-f file | 將 file 中的命令添加到已有的命令中 |
-n | 不產生命令輸出,使用 print 來完成輸出 |
1. 在命令行定義編輯器命令
默認情況下,sed
編輯器直接從 STDIN
讀取流數據,然后匹配命令執行編輯操作。如下:
$ echo "This is a test" | sed 's/test/big test/'
This is a big test
$
這個例子使用了 s
命令。s
命令是字符串替換命令。上面的例子是單行數據,sed
也可以處理多行數據。
$ cat data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$
$ sed `s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
$
sed
幾乎是瞬間就執行完成并返回數據。并且 sed
并不會修改原始文件。如果查看原來的文件,它仍然保留這原始數據
$ cat data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$
2. 在命令行使用多個編輯器命令
要在命令行上使用多個 sed
命令,使用 -e
選項就可以了。
$ sed -e 's/brown/green/; s/dog/cat/' data1.txt
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
$
上面的2個命令都會作用到每一行中。使用多個命令時命令之間必須用分號分開,并且在命令末尾和分號之間不能有空格。如果不想使用分號,也可以使用bash
中的次提示符來分隔命令。只有輸入第一個單引號標示出sed
程序腳本的起始,bash
就會繼續提示你輸入更多命令,直到輸入了標示結束的單引號。
$ sed -e '
> s/brown/green/
> s/dog/cat/' data1.txt
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
$
結果和單行輸入的命令一樣。必須記住,要在末尾單引號所在行結束命令。bash
一旦發現了結束的單引號就會執行命令。
3. 從文件中讀取編輯器命令
如果有大量的sed
需要處理,把它們放到一個單獨的文本文件中是個不錯的選擇。可以在sed
命令中使用- f
來制定命令文件.
$ cat script1.sed
s/brown/green/
s/fox/elephant/
s/dog/cat/
$
$ sed -f script1.sed data1.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
$
在這種情況下,不用在每條命令后面放置分號。sed
從指定的文件中讀取命令,并把它們應用到數據文件的每一行上面。
sed
基礎
成功使用sed
的關鍵在于掌握各式各樣的命令和格式。下面就來介紹下sed
中的各個基本的命令和功能。
1. 替換命令
1.1 替換標記
默認情況下sed
只替換每行中第一處匹配的字符串。如果想替換一行中不同的地方就需要使用替換標記(substitution flag
)。替換標記在替換命令之后設置。
s/pattern/replacement/flags
有一下4中可以使用的替換標記:
- 數字:表明替換第幾處匹配的地方
- g: 替換所以匹配的文本
- p: 打印原先行的內容
- w file: 將替換的結果寫到文件中
下面看看這4中標記的效果:
$ cat data2.txt
This is a test of the test script.
This is the second test of the test script.
$
$ sed 's/test/trial/' data2.txt
This is a trial of the test script.
This is the second trial of the test script.
$
原始的不使用替換標記的命令,上面的輸出中只替換了每行第一個匹配到的test
。
$ sed 's/test/trial/2' data2.txt
This is a test of the trial script.
This is the second test of the trial script. $
$
使用數字2
來當作替換標記,替換了每行中第二個匹配到的test
。
$ sed 's/test/trail/g' data2.txt
This is a trial of the trial script.
This is the second trial of the trial script.
$
g
標記會替換所以匹配到的字符串.
$ cat data3.txt
This is a test line.
This is a different line.
$
$ sed -n 's/test/trial/p' data3.txt This is a trial line.
$
p
標記會打印匹配到的行。通常會和-n
選項一起使用。
w替換標記會產生同樣的輸出,不過會將輸出保存到指定文件中。
$ sed 's/test/trial/w test.txt' data3.txt
This is a trial line.
This is a different line.
$
$ cat test.txt
This is a trial line.
$
sed
的正常輸出是在STDOUT
,只有那些匹配到的行才會保存到指定的文本文件中。
1.2 替換字符
有時會在字符串中遇到不太方便處理的字符,比如Linux
下的/
。替換路徑中的/
就比較麻煩,必須使用\
來轉義:
$ sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
要解決這個問題,sed
允許使用其它字符來作為替換命令中的字符串分隔符:
$ sed 's!/bin/bash!/bin/csh!' /etc/passwd
這個例子使用感嘆號作為字符串分隔符,這樣路徑名就容易閱讀和理解了。
2. 使用地址
默認情況下,sed
中的命令會作用于所有的數據行,但是如果只想將命令作用于某些特定的行,則必須使用行尋址(line addressing
)
sed
中有兩種行尋址:
- 以數字形式表示行區間
- 以文本模式來過濾出行
以上兩種形式都使用相同的格式來制定行地址:
[address]command
也可以將特定地址的多個命令分組:
address {
command1
command2
command3
}
sed
會將特定的命令作用于指定的行上。
2.1 使用數字方式的行尋址
使用數字方式的尋找時,可以使用行在文本流中的位置來引用。文本流中第一行編號為1
,然后按順序分配行號。
在命令中使用行號時可以指定單個行號,或是用起始行號、逗號以及結尾行號指定的一定區間范圍的行。
$ sed '2s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
$
上面的例子使用的是單個行號,sed
只修改了第二行的內容。
$ sed '2,$s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy cat
$
上面使用了行地址區間。并且$
代表從某行開始的所有行。
2.2 使用文本模式的行尋址
sed
編輯器允許指定文本模式來過濾出命令有要作用的行。格式如下:
/pattern/command
必須用正斜線將制定的pattern
封起來。其實就是使用正則表達式(regular expression
)來過濾文本行. 正則表達式允許創建高級文本模式來匹配各種數據。這種表達式結合了一些列通配符、特殊字符以及固定文本字符來匹配幾乎任何形式的文本的簡練模式。
$ grep Samantha /etc/passwd
Samantha:x:502:502::/home/Samantha:/bin/bash
$
$ sed '/Samantha/s/bash/csh/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[...]
Christine:x:501:501:Christine B:/home/Christine:/bin/bash
Samantha:x:502:502::/home/Samantha:/bin/csh
Timothy:x:503:503::/home/Timothy:/bin/bash
$
上面是一個使用簡單的文本模式匹配的例子。關于復雜的正則表達式的內容請看相關文章。
2.3 命令組合
如果需要在單行上執行多條命令,可以用花括號將多條命令組合在一起。sed
編輯器會在地址行處執行每條命令。
$ sed '2{
> s/fox/elephant/
> s/dog/cat/
> }' data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown elephant jumps over the lazy cat.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$
兩條命令都會作用在第二行。當然也可以使用地址區間:
$ sed '3,${
> s/brown/green/
> s/lazy/active/
}' data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick green fox jumps over the active dog.
The quick green fox jumps over the active dog.
$
sed
會將上面的兩條命令作用于第三行之后的所有行上。
2 刪除行
除了替換文本,sed
也可以刪除文本流中的特定行。sed
得刪除命令是d
,但是使用該命令要非常小心,如果忘記在命令中加上尋址符的話,流中的文本都會被刪除。
$ cat data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog
$
$ sed 'd' data1.txt
$
當和尋址符一起使用時,sed
才能發揮最大的功用。通過指定行號,sed
可以從文本流中刪除指定的行:
$ cat data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
$
$ sed '3d' data6.txt
This is line number 1.
This is line number 2.
This is line number 4.
$
也可以通過地址區間指定要刪除的行區間:
$ sed '2,3d' data6.txt
This is line number 1.
This is line number 4.
$
或者特殊的文件結尾字符:
$ sed '3,$d' data6.txt
This is line number 1.
This is line number 2.
$
sed
的尋址符也可以使用模式匹配:
$ sed '/number 1/d' data6.txt
This is line number 2.
This is line number 3.
This is line number 4.
$
還可以使用文本匹配模式來刪除某個區間的所有行。但是這么做要非常小心,第一個文本模式會"打開"行刪除功能,第二個模式會"關閉"行刪除功能。
$ sed '/1/,/3/d' data6.txt
This is line number 4.
$
另外你也要特別小心,sed
只要在文本行中匹配到了開始模式,刪除功能就會打開:
$ cat data7.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
This is line number 1 again.
This is text you want to keep.
This is the last line in the file.
$
$ sed '/1/,/3/d' data7.txt
This is line number 4.
$
在第二次匹配到1
后,行刪除功能以及打開,因為沒有在文本流中匹配到停止模式,所以刪除功能就把剩余的行全部刪除了。如果指定了開始模式,但是指定了一個無法匹配文本流中任何行的停止模式,這就會出現另外的問題:
$ sed '/1/,/5/d' data7.txt
$
因為刪除功能在匹配到第一個模式的時候就打開了,但是沒有匹配到第二個模式,所以整個文本流都被刪除了。
3. 插入和增加文本
和其他編輯器類似,sed
也可以向數據流中插入和添加文本行。
- i(insert): 在指定行前面增加一個新行
- a(append): 在指定行后面增加一個新行
這兩個命令不能在單個命令行上使用。格式如下:
sed '[address]command\
new line'
new line
中的文本會出現在指定的位置。當使用插入命令時,文本會出現在數據流文本的前面。
$ echo "This is a test line" | sed 'i\This is a insert line'
This is a insert line
This is a test line
$
$ echo "This is a test line" | sed 'a\This is a append line'
This is a test line
This is a append line
$
如果不指定地址,sed
會在每行數據流文本前面或者后面增加或追加指定的文本。一般情況下我們只是希望在指定行的前面或者后面添加文本,這時候可以指定一個地址,可以使用數字或者匹配模式,但是不能使用地址區間。因為追加文本只能以行為單位,而不能追加到區間的前面或后面。
下面講的例子是講文本行添加到第3
行前面和后面:
$ sed '3i\
This is an insert line.' data6.txt
This is line number 1.
This is line number 2.
This is an inserted line.
This is line number 3.
This is line number 4.
$
$ sed '3a\
This is an appended line.' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an appended line.
This is line number 4.
$
如果想在文本流的開始和結束位置添加文本行,可以使用1
和$
來指定文本流的開始和結束,使用i
或a
在文本流的開始和末尾添加文本。
如果想一次行添加多行文本,可以在每行后面使用反斜線,直到最后以行。
$ sed '1i\
> This is the first insert line.\
> And this is the second insert line.' data6.txt
This is the first insert line.
And this is the second insert line.
This is line number 1.
This is line number 2.
This is line number 3.
This is an appended line.
This is line number 4.
$
4. 修改行
sed
中的c
命令支持修改數據流中的整行文本。它跟i
和a
的工作機制一樣,必須在命令中指定新行。
$ sed '3c\
> This is a changed line.' data6.txt
This is line number 1.
This is line number 2.
This is a changed line of text.
This is line number 4.
$
c
命令指出單地址也指出地址區間,也可以使用文本匹配。如果是地址區間,則會把地址區間中的文本變為指定的文本行。
$ sed '/number 1/,/number 3/c\
> This is a new line.' data6.txt
This is a new line.
this is line number 4.
$
記住:sed
是一個流編輯器。
5. 轉換命令
轉換(transform
)命令(y
)是唯一一個可以處理單個字符的sed
命令。格式如下:
[address]y/inchars/outchars/
轉換命令會對inchars
和outchars
值進行一對一映射。inchars
中的第一個字符會被轉換為outchars
中的第一個字符,inchars
中的第二個字符會被轉換為outchars
中的第二個字符,依次類推。所有如果inchars
的長度和outchars
長度不同,則sed
會產生一條錯誤消息。
$ sed 'y/123/789' data8.txt
This is line number 7.
This is line number 8.
This is line number 9.
This is line number 4.
This is line number 7 again.
This is yet another line.
This is the last line in the file.
$
轉換命令是一個全局命令,也就是說它會在文本行中替換所有的指定字符,而不考慮它們出現的位置。
$ echo "There is 1 cat and 1 dog." sed 'y/123/456/'
There is 4 cat and 4 dog.
$
你無法限定只轉換特定地方的特定字符,sed
會轉換所有匹配到的字符。
sed
基礎的思維導圖,可以參考下圖:
