第二十章 文本處理
所有類UNIX系統都嚴重依賴于文本文件來存儲數據,所以存在大量文本操作工具也在情理之中。
相關命令:
- cat:拼接文件。
- sort:排序文本行。
- uniq:報告或忽略重復的行。
- cut:從每行中刪除部分內容。
- paste:合并行。
- join:連接兩個文件中具有公共字段的行。
- comm:逐行比較兩個已排序的文件。
- diff:逐行比較文件。
- patch:對原文件應用diff文件。
- tr:轉寫或刪除文件。
- sed:用于文本過濾和轉換的流編輯器。
- aspell:交互式拼寫檢查器。
文本的應用
到目前為止,學習過兩種文本編輯器(Nano和Vim),看過多個配置文件,也看到過許多命令的輸出結果,它們都離不開文本。除此之外,文本還應用到很多地方。
文檔
很多人都使用純文本格式編寫文檔。雖然不難看出用小文本文件記錄些簡單的筆記確實有用,但編寫文本格式的大型文檔也是可行的。有一種流行的方式就是先采用文本格式編寫大型文檔,然后在其中嵌入標記語言來描述最終文檔的格式。許多科技論文就是這樣寫出來的。
網頁
世界上流行的電子文檔類型包括網頁了。網頁是使用超文本標記語言(Hypertext Markup Language, HTML)或可擴展標記語言(Extensible Markup Language, XML)來描述文檔可視格式的文本文檔
電子郵件
電子郵件本質上是一種基于文本的媒介。就算是非文本附件,在傳輸的時候也會被轉換為文本格式。郵件消息以標題(header)開頭,描述了該郵件的來源以及其傳輸過程中所進行的處理,然后是郵件消息正文(body)。
打印機輸出
在類UNIX系統中,輸出到打印機的消息是以純文本格式發送的,如果待打印頁面包含圖像,則將其轉換為稱作PostScript的文本格式頁面描述語言的描述信息,然后發送到負責生成待打印圖形點(graphic dots)程序
程序源代碼
類UNIX系統中有不少命令行程序都是為了支持系統管理和軟件開發而編寫的,文本處理程序也不例外,其中有很多是為解決軟件開發問題而設計的。文本處理對軟件開發者重要的原因在于所有的軟件一開始都是文本。軟件開發者實際編寫的程序源代碼始終都是文本格式。
溫故知新
在第6章中,提到了一些除命令行參數之外還能接受標準輸入的命令。不過當時只是"一筆帶過",現在詳細了解這些命令如何進行文本處理。
cat——連接文件并打印
cat命令中,有不少選項可用于增強文本內容的視覺效果。-A就是一個例子,它能夠顯示文本中的非輸出字符。有時想知道可見文本中是否嵌有控制字符。常見的控制字符就是制表符和回車符,后者多見于MS-DOS風格的文本文件的行尾。另一種常見情況是文本文件中包好末尾帶有空格的文本行。
將cat命令作為一個簡單的文本處理器。輸入了一個以制表符開頭,若干空格結尾的文本行。
以默認方式輸出文本:
cat > foo.txt
輸出內容如下:
The quick brown fox jumped over the lazy dog.
使用帶有-A選項的cat顯示文本:
cat -A foo.txt
輸出內容如下:
^IThe quick brown fox jumped over the lazy dog. $
可以看到,文本中的制表符由^I表示。這是一種常見表示法,代表Ctrl-I。還可以看到$出現在真正的行尾,這說明文本中包含結尾空格。
有些cat選項可用于修改文本,其中較突出的兩個分別是-n或-s,前者能夠為文本行加上行號,后者能夠禁止輸出連續的空白行。
例如:
[me@linuxbox ~]$ cat > foo.txt
The quick brown fox
jumped over the lazy dog.
[me@linuxbox ~]$ cat -ns foo.txt
1 The quick brown fox
2
3 Jumped over the lazy dog.
在這個例子中,我們創建了一個新版本的foo.txt測試文件,其中包含被兩個空白分隔的兩行文本。經cat的-ns選項處理之后,多余的空白行被刪除了,剩下的文本行也被加上了行號。雖然這算不上復雜的處理過程,但文本的確經過了處理。
sort-排序
sort對標準輸入內容或命令行上指定的文件進行排序,并將結果發送至標準輸出。
例如:對文件foo.txt進行排序:
foo.txt的內容:
c
a
b
sort foo.txt
輸出結果如下:
a
b
c
從輸出結果可以看出,sort是按字典序對字母進行排序的。
sort可以接受命令行上指定的多個文件作為參數,所以能將多個文件合并成單個文件。
例如:假設有3個文本文件,我們想將其合并成一個有序文件,可以這樣做:
sort file1.txt file2.txt file3.txt > final_sorted_list.txt
常用的sort選項
選項 | 描述 |
---|---|
-b, --ignore-leading-blanks | 在默認情況下,對整行進行排序,從行內的第一個字符開始。該選項使sort忽略每行開頭的空白字符,從第一個非空白字符開始排序 |
-f, --ignore-case | 排序時不區分大小寫 |
-n, --numeric-sort | 根據字符串的數值(numberic evaluation)排序。該選項使排序按照數值順序,而不按照字母表順序進行 |
-r, --reverse | 降序排序。輸出結果按照降序排列 |
-k, --key=field1[,field2] | 不再按照整行,而是按照由區間范圍[field1,field2]指定的關鍵字字段進行排序。 |
-m,–merge | 每個參數被視為預排序過的文件名稱。將多個文件合并單成單個有序結果,不再執行額外的排序 |
-o, --output=file | 將排序后的輸出發送至file,而非標準輸出 |
-t, --field-separator=char | 定義字段分隔符。在默認情況下,字段由空格符或制表符分隔 |
例如:
對數值數據進行排序,需要通過du命令獲取各個目錄的占用磁盤空間,du命令格式如下:
du -s path #統計path下各個字母錄所占用的磁盤空間。
對/usr/share下的子目錄的占用磁盤空間進行降序排序:
du -s /usr/share/* | sort -nr | head
命令結果如下:
509940 /usr/share/local-langpack
242660 /usr/share/doc
197560 /usr/share/fonts
179144 /usr/share/gnome
146764 /usr/share/myspell
144304 /usr/share/gimp
135880 /usr/share/dict
76508 /usr/share/icons
68072 /usr/share/apps
62844 /usr/share/foomatic
通過-n和-r選項,按照數值進行降序排序,使最大值出現了結果最前面。之所以可行的原因在于數值位于每行的開頭位置。如果像根據行內的某個值來排序。下面是ls -l的輸出結果:
[me@linuxbox ~]$ ls -l /usr/bin | head
total 152948
-rwxr-xr-x 1 root root 34829 2016-04-04 02:42 [
-rwxr-xr-x 1 root root 101556 2007-11-27 06:08 a2p
-rwxr-xr-x 1 root root 13036 2016-02-27 08:22 aconnect
-rwxr-xr-x 1 root root 10552 2007-08-15 10:34 acpi
-rwxr-xr-x 1 root root 3800 2016-04-14 03:51 acpi_fakekey
-rwxr-xr-x 1 root root 7536 2016-04-19 00:19 acpi_listen
-rwxr-xr-x 1 root root 3576 2016-04-29 07:57 addpart
-rwx-xr-x 1 root root 20808 2016-01-03 18:02 addr2line
-rwx-xr-x 1 root root 489704 2016-10-09 17:02 adept_batch
使用sort進行文件大小排序:
[me@linuxbox ~]$ ls -l /usr/bin | sort -nrk 5 | head -n 5
-rwxr-xr-x 1 root root 8234216 2016-04-07 17:42 inkscape
-rwxr-xr-x 1 root root 8222692 2016-04-07 17:42 inkview
-rwxr-xr-x 1 root root 3746508 2016-03-07 23:45 quanta
-rwxr-xr-x 1 root root 3654020 2016-08-26 23:45 gimp-2.4
-rwxr-xr-x 1 root root 2928760 2016-09-10 14:31 gdbtui
sort的許多用法都涉及表格型數據(tabular data)處理,例如上述ls命令的輸出結果。如果套用數據庫的術語,可以稱每行是一條記錄,每列是一個字段,諸如文件屬性、鏈接數、文件名、文件大小等。sort能夠單獨的字段,用數據庫的術語來說,可以指定一個或多個關鍵字段(key fields)作為排序關鍵字(sort keys)。指定了-n和-r選項,按照數值進行降序排序,同時還制定了-k 5,使sort用第5個字段作為排序關鍵字。
-k選項具備不少特征,這些特征值得注意,先介紹sort定義字段的。考慮一個簡單的文本文件,其中只有一行內容,包含作者和姓名:
William Shotts
在默認情況下,sort將該行視為兩個字段。第一個字段包含字符串Willian,第二個字段包含字符串Shotts。
這意味著空白字符(空格符和制表符)被用作字段之間的分隔符,在進行排序時,分隔符包括在字段中。
ls輸出包含8個字段,其中第5個字段是文件大小:
-rwxr-xr-x 1 root root 8234216 2016-04-07 17:42 inkscape
sort允許出現多個-k選項,這樣救就能指定多個排序關鍵字。關鍵字可以是字段范圍。如果沒有指定范圍,則從指定字段開頭,一直延續到行尾。
采用短選項-k 1,1 -k 2n,將排序限制在第一個字段中,所以指定1,1,意思時"從字段1開始,至字段1結束“。第二個-k選項指定了2n,意思是字段2作為排序關鍵字且按照數值進行排序。這些選項字母和sort的全局選項一樣:b(忽略開頭的空白字符)、n(數值排序)、r(降序排序)等。
列表中第3個字段包含的日期格式不適合排序。在計算機中,日期通常采用年-月-日的形式,以便按時間順序進行排序,但這里采用的是月/日/年格式。sort提供了-k選項允許指定字段偏移,這樣就可以指定關鍵字了:
sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt
該命令輸出結果如下:
Fedora 10 11/25/2008
Ubuntu 8.10 10/30/2008
SUSE 11.0 06/19/2008
Fedora 9 05/13/2008
Ubuntu 8.04 04/24/2008
Fedora 8 11/08/2007
Ubuntu 7.10 10/18/2007
通過指定-k 3.7, 告知sort排序關鍵字從第3個字段的第7個字符開始(對應于年份)。與此類似,分別指定-k 3.1和 -k 3.4,劃分出日期中的月和日,另外還加入了-n和-r選項來實現數值降序排序。-b選項用于禁止日期字段開頭的空格。
sort提供了 -t選項來定義字符分隔符。
例如:按照第七個字段排序passwd文件,可以這樣做:
sort -t ':' -k 7 /etc/passwd | head
命令輸出如下:
me?1001:1001:Myself,:/home/me:/bin/bash
root?0:0:root:/root:/bin/bash
dhcp?101:102::/nonexistent:/bin/false
gdm?106:114:Gnome Display Manager:/var/lib/gdm:/bin/false
hplip?104:7:HPLIP system user,:/var/run/hplip:/bin/false
klog?103:104::/home/klog:/bin/false
messagebus?108:119::/var/run/dbus:/bin/false
polkituser?110:122:PolicyKit,:/var/run/PolicyKit:/bin/false
pulse?107:116:PulseAudio daemon,:/var/run/pulse:/bin/false
將冒號指定為字段分隔符。
uniq-刪除重復行
uniq執行的任務較簡單,即指定一個排序過的文件,它會刪除其中所有重復的行并將結果發送至標準輸出。uniq經常配合sort來清楚重復輸出。
命令如下:
uniq filename #去重 filename內容中的相同的行寫到標準輸出sort filename | uniq #對排序后的filename的內容去重寫到標準輸出
盡管uniq屬于傳統的UNIX工具,多與sort配合使用,而GNU版本的sort支持-u選項,同樣能夠刪除有序輸出中的重復行
例如對文件foo.txt進行去重
sort foo.txt | uniq
foo.txt文件內容:
a
b
c
a
b
c
命令輸出結果如下:
a
b
c
從結果可以看出uniq可以去重,但是要注意uniq只能刪除連續的重復行因此需要通過sort進行排序后調用uniq
常用uniq選項
選項 | 描述 |
---|---|
-c, --count | 輸出重復的行,在其之前加上改行重復出現的次數 |
-d, --repeated | 只輸出重復的行,不包括非重復行 |
-f n, --skip-fields=n | 忽略每行前n個字段。和sort一樣,字段以空白字符分隔;但不同于sort,uniq沒有能夠設置其它字段分隔符的選項 |
-i, --ignore-case | 對比內容的時候忽略大小寫敏感 |
-s n, --skip-chars=n | 跳過(忽略)每行開始的前n個字符 |
-u,–unique | 只輸出不重復的行。忽略重復行 |
例如: 使用-c選項報告文件foo.txt中重復行的數量
命令如下:
sort foo.txt | uniq -c
命令輸出結果如下:
2 a
2 b
2 c
切片和切塊
接下來的命令可用于從文件中提取文本列,再以其它方式重新組合。
cut-從每行中刪除部分內容
cut能夠從每行提取部分文本并將其輸出至標準輸出。
常用的cut選項
選項 | 描述 |
---|---|
-c list, --characters=list | 提取由list定義的部分行。list可以是一個或多個逗號分隔的數值范圍 |
-f list, --fields=list | 從行中提取由list定義的一個或多個字段。list可以包含一個或多個字段,抑或是逗號分隔字段范圍 |
-d delim, --delimiter=delim | 如果制定了-f,則使用delim作為字段分隔符。在默認情況下,字段必須單個制表符分隔 |
–complement | 提取除-c或-f指定部分之外的文本行 |
cut適合從其他程序產生的文件中提取文本,而不是從用戶輸入中直接提取。
例如:利用-f選項提取由制表符分隔的第3個字段.。
用cat -A查看distros.txt文件內容:
可以看出字段間分隔符號為制表符。
使用-f選項提取字段:
cut -f 3 distros.txt
命令結果如下:
由于文件采用制表符作為分隔符,因此最好使用cut提取自字段而不是字符。
在上個例子中,提取的字段包含相同長度的數據,因此可以通過提取每行中的年份來提取字符:
命令如下:
cut -f 3 distros.txt | cut -c 7-10
命令結果如下:
再次對distros.txt執行cut,就可以提取7-10個字符,對應于日期中的年份。
在處理字段時,可以指定不同的字段分隔符。例如從/etc/passwd文件中提取第一個字段:
cut -d ':' -f 1 /etc/passwd | head
命令輸出結果如下:
本例中,/etc/passwd以冒號分隔字段,cut通過-d選項指定冒號作為分隔符,通過-f選項指定第一個字段。
paste-合并行
paste將文本添加列添加到文件中。通過讀取多個,然后將每個文件中的字段合并來實現的。
格式如下:
paste file1 file2 ... #按照順序將file1,file2等文件的內容橫向排列
例如:將發行日期放在發行版本名詞之前,
先基于distros.txt生成幾個文件:
sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt > distros-by-date.txtcut -f 1,2 distros-by-date.txt > distros-versions.txtcut -f 3 distros-by-date.txt > distros-dates.txt
其內容如下:
使用paste將發行日期放在發行版本名詞之前,形成按照順序排列的發行清單:
paste distros-dates.txt distros-versions.txt
命令輸出結果如下:
join-連接兩個文件中具有公共字段的行
在某些方面join和paste一樣,將文本列添加到文件中,只不過前者采用了一種獨特的方式。連接(join)操作常見于關系數據庫,用于將多個數據表中具有共享關鍵字字段(shared key field)的數據合并在一起,形成所需的結果。join命令根據關鍵字連接多個文件的數據。
例如通過join命令將發行日期放在發行版本之前:
首先生成幾個文件:
cut -f 1,1 distros-by-date.txt > distros-names.txtpaste distros-dates.txt distros-names.txt > distros-key-names.txtcut -f 2,2 distros-by-date.txt > distros-vernums.txt
paste distros-dates.txt distros-vernums.txt > distros-key-vernums.txt
文件內容如下:
現在有了兩個具有共享關鍵字的文件。要指出的重要一點是文件必須依據關鍵字段排序,這樣join才能正常工作:
join distros-key-vernums.txt distros-key-names.txt | head
輸出結果如下:
默認情況下,join使用空白字符作為輸入字段分隔符,單個空格符作為輸出字段分隔符。可以指定相關選項修改規則,詳見join手冊頁。
比較文本
比較文本文件的不同版本。對系統管理員和軟件開發者而言,尤其重要。
comm-逐行比較兩個已排序的文件
comm命令比較兩個文本文件,顯示各自獨有的行和共有的行。
命令如下:
comm file1 file2 #逐行比較file1和file2,其輸出結果第一列是file1獨有的行,第二列是file2獨有的行,第三列是file1和file2共有的行
例如:比較file1.txt和file2.txt文件
file1.txt的文件內容:
a
b
c
d
file2.txt的文件內容
b
c
d
e
使用comm比較這兩個文件:
comm file1.txt file2.txt
命令輸出結果如下:
可以看出a是file1獨有的內容,e是
file2獨有的內容,b,c,d是file1和file2共有的內容。
comm還支持-n選項,其中,n可以是1、2或3。在使用時,該選項制定了要禁止顯示那些列。
例如:只顯示兩個文件共有的行,可以禁止輸出第一列和第二列:
comm -12 file1.txt file2.txt
命令輸出結果如下:
diff-逐行比較文件
diff可用于檢測文件之間的差異。支持多種輸出形式,能夠一次處理大量文本文件。軟件開發人員經常diff檢測不同版本的源代碼之間的差異,進而遞歸檢查整個源代碼目錄(通常稱為源代碼樹)。diff的常見用法是創建diff文件或補丁,共patch等其他程序使用。
使用diff查看上一個例子中的文件:
diff file1.txt file2.txt
命令輸出如下:
其輸出格式只簡要描述了兩個文件之間的差異。在默認格式中,每組變動之前都會有一個形如"范圍 操作 范圍” (range operation range)的變動命令(change command),它用于描繪將第一個文件轉換為第二個文件所需的位置和變動類型。
diff的變動命令
命令 | 描述 |
---|---|
r1ar2 | 將第二個文件中位于r2處的行追加到第一個文件中的r1處 |
r1cr2 | 將第一個文件中位于r1處的行修改(替換)為第二個文件中位于r2處的行 |
r1dr2 | 刪除第一個文件中位于r1處的行,該行也出現在第二個文件中r2處 |
這種格式中,范圍是由逗號分隔的開頭行和終止行組成的。作為默認格式(主要是為了POSIX合規性以及向后兼容傳統的UNIX版本的diff),它并沒有另外幾種可選格式應用得那么廣泛。上下文格式(context format)和合并格式(unified format)是兩種更為流行的格式。
上下文格式(-c選項)如下所示:
diff -c file1.txt file2.txt
命令輸出結果如下:
輸出結果以兩個文件的名稱及其時間戳作為開頭。第一個文件用星號標記,第二個文件用連字符標記。在剩下的輸出結果中這兩種標記分別代表對應的文件。接下來,會看到若干組變動,其中包括上下文行號。第一組中:
*** 1, 4 ***
表明第一個文件中的第1行到第4行。隨后會看到:
— 1, 4 —
表明第二個文件中的第1行到第4行。
diff上下文格式的變動指示符
指示符 | 含義 |
---|---|
無 | 上下文行。并不表示兩個文件之間的差異 |
- | 要刪除的行。該行出現在第一個文件,但不出現在第二個文件中 |
+ | 要添加的行。該行出現在第二個文件,但不出現在第一個文件中 |
! | 要改動的行。該行的兩個版本都會顯示,各自出現在變動組相應的區域中 |
合并格式和上下文格式類似,但更簡明。該格式由-u選項指定。
命令如下:
diff -u file1.txt file2.txt
命令輸出如下:
上下文格式和合并格式之間較明顯的區別在于后者去除了重復的上下文行,使合并格式的輸出結果比上下文格式更為精簡。可以看到和上下文格式一樣的時間戳,后面緊跟著字符串@@-1,4 +1,4 @@。指明了變動組內第一個文件中的行和第二個文件中的行。接下來便是各行本身以及默認的3行上下文。
diff合并格式的變動指示符
指示符 | 含義 |
---|---|
無 | 該行為兩個文件所共有 |
- | 該行要從第一個文件中刪除 |
+ | 該行要加入第一個文件 |
patch-對原文件應用diff文件
patch命令能夠對文本文件應用改動。它接受diff輸出,一般用于將舊版本的文件轉換為新版本。例如Linux內核是由一支規模龐大、組合松散的貢獻者團隊開發的,他們持續不斷地向源代碼提交細小的改動。如果某個開發人員做一次改動,就要向每位開發人員發送整個內核源代碼樹,這種做法并不切實際。實際上只要發送diff文件就夠了。diff文件包含了內核新、舊版本之間的改動內容。接受者只需使用patch命令將這些改動應用于自己的內核源代碼即可。diff/patch提供了兩個重要優勢。
- 相較于整個源代碼樹,diff文件要小得多。
- diff文件簡潔地描述了所做的改動,便于查閱人員快速地對該文件做出評估。
diff/patch能夠處理任何文本文件,并不局限于源代碼。二者同樣適用于配置文件或其他文件。
為了生成可供patch使用的diff文件,GNU文檔建議按照下列方式使用diff:
diff -Naur old_file new_file > diff_file
其中,old_diff和new_diff可以是單個文件,也可以是包含多個文件的目錄。-r選項能夠對目錄樹執行遞歸操作。
只要生成了diff文件,就可以將diff應用于舊文件,使其成為新版本的文件:
patch < diff_file
例如將fiel1.txt的內容更改為file2.txt的內容:
生成diff_file文件:
diff -Naur file1.txt file2.txt > patchfile.txt
使用patch通過patchfile.txt文件將file1.txt的內容更新為file2.txt的內容。
patch < pachfile.txt
命令輸出結果如下:
查看更改后的file1.txt的內容:
即時編輯
文本編輯器大多是交互的,也就是需要移動光標的,然后輸入內容,還有非交互的文本編輯方式。
tr-轉寫或刪除字符
tr命令可用于轉寫(transliterate)字符。可以將其視為某種基于字符的“搜索-替換”操作。轉寫是將字母表中的一個字母更改為另一個字母的過程。
命令格式如下:
tr target #
tr接受兩個參數:源字符集合和目標字符集合。有3種方法表示字符集合:
- 枚舉列表,例如ABCDEFGHIJKLMNOPQRSTUVWXYZ。
- 字符范圍,例如A-Z。
- POSIX字符集,例如[:upper:]。
例如將多個字符轉換為單個字符:
echo "lowercase letters" | tr [:lower:] A
命令輸出結果如下:
除了轉寫,將MS-DOS文本轉換為UNIX文本的問題,這需要刪除每行行尾的回車符。可以通過tr命令來實現:
tr -d '\r' < dos_file > unix_file
其中dos_file是要被轉換的文件,unix_file是轉換后的結果。這種命令形式使用了轉義字符\r來表示回車符。
tr可以使用-s選項能夠"擠壓"(刪除)重復出現的字符。例如:
echo "aaabbbccc" | tr -s ab
命令輸出結果如下:
注意重復字符必須是連續的,如果不是連續的,則不會有擠壓效果:
echo "abcabcabc" | tr -s ab
命令輸出結果如下:
可以看出并沒有產生擠壓。
sed-用于文本過濾和轉換的流編輯器
sed是流編輯器(stream editor)的簡稱。它可以對文本流(一組指定的文件或標準輸入)進行編輯。sed功能強大且比較復雜。
sed的工作方式:為其指定單個編輯命令(在命令行中)或者包含多個命令的腳本文件名,然后對文本流中的每行文本執行這些命令。下面是一個示例:
echo "front" | set 's/front/back/'
命令輸出結果如下:
back
從結果可以看出這是個查找-替換操作,將front替換為back,在set的參數中s是編輯命令,front和back是s的參數,front是查找的字符串,back是替換的字符串。
可以給sed命令之前可以添加一個地址,用于指定要編輯輸入流中的那些行。
例如:
[me@linuxbox ~]$ echo “front” | sed ‘1s/front/back/’
back
[me@linuxbox ~]$ echo “front” | sed ‘2s/front/back/’
第一條命令指定對第一行操作,因為輸入為front,所以輸出back。第二條命令無輸出,是因為對第二行進行操作,但輸入只有一行,故沒有輸出。
sed地址方式表示
地址 | 描述 |
---|---|
n | 行號,其中n為正整數 |
$ | 最后一行 |
/regexp/ | 匹配POSIX BRE的行。注意,正則表達式由正斜線分隔。正則表達式也可以選擇使用其他字符分隔,這需要使用\cregxpc來指定正則表達式,其中的c就是分隔符 |
addr1,addr2 | 從addr1至addr2的行范圍(包括addr1和addr2)。地址可以是前面所述的任何一種地址形式 |
first~step | 匹配從行號first開始,然后間隔依次為step的那些行。例如1~2指代所有奇數行,5~5指代第5行和之后所有是5倍數的行 |
addr1,+n | 匹配addr1和接下來的n行 |
addr! | 匹配除addr之所有行,addr可以是前面所述的任何一種地址形式 |
演示各種地址表示方式。來看地址范圍:
sed -n '1,5p' distros.txt
輸出結果如下:
本例中,輸出范圍從第1行~第5行。因此用到了p命令,改名令只是簡單的輸出匹配行。還必須加入-n(no auto-print選項)選項,使sed不默認輸出所有行。
使用正則表達式:
sed -n '/SUSE/p' distros.txt
輸出命令如下:
通過加入正斜線分隔的正則表達式/SUSE/,就能類似于grep那樣的方法提取出匹配的行。
最后在地址加入!,嘗試排除操作:
sed -n '/SUSE/!p' distros.txt
命令輸出結果如下:
至此介紹了兩個sed編輯命令s和p。
sed基本編輯命令
命令 | 描述 |
---|---|
= | 輸出當其行行號 |
a | 將文本追加到前行之后 |
d | 刪除當前行 |
i | 將文本插入當前行之前 |
p | 輸出當前行。在默認情況下,sed會輸出所有行,只編輯文件中匹配指定地址的那些行。通過-n選項,可以拒絕該默認行為 |
q | 退出sed,不再處理剩余的。如果未指定-n選項,會輸出當前行 |
Q | 退出sed,不再處理剩余的行 |
s/regexp/replacement/ | 將regexp匹配的地方替換成replacement。replacement可以包含特殊字符&,其代表regexp所匹配到的文本。除此之外,replacement也可以包含序列\1~\9,其代表regexp中對應的子表達式所匹配到的文本。在后文討論向后引用的時候,會更詳細地說明。在replacement之后的結尾正斜線處,可以指定一些能夠改變s命令行為的可選標志 |
y/set1/set2 | 通過將set1中的字符更換成set2中對應的字符來執行轉寫。注意,和tr不同的是,sed要求set1和set2這兩個字符集合的長度必須相同 |
盡管日期經過了格式化,但如果采用年-月-日的格式會更好(便于排序)。通過sed命令可以實現,其命令如下:
sed 's/\([0-9]\{2\}\)\/\([0-9]\{2\}\)\/\([0-9]\{4\}\)$/\3-\1-\2/' distros.txt
命令輸出如下:
sed命令的基本結構如下:
sed ‘s/regexp/replacement/’ distros.txt
因為日期采用的格式為月/日/年且出現在行尾,所以可以這樣寫:
[0-9]{2}/[0-9]{2}/[0-9]{4}$
該正則表達式匹配2個數位、1個斜線、2個數為、1個斜線、4個數位。regexp部分搞定。
在一些使用了ERE的應用中,包含“向后引用”特征,工作方式是這樣的:如果\n出現在replacement中,這里的n是1~9中的一個數字,則此序列指代的是之前的正則表達式中對應的子表達式匹配到內容。只用將需要部分放入括號中即可:
([0-9]{2})/([0-9]{2})/([0-9]{4})$
現在得到3個子表達式。第一個包含月份,第2個包含月份中的天數,第三個包含年份。可以構建出下列replacement:
\3-\1-\2
這樣就得到了年份、連字符、月份、連字符、天數。
于是整個命令如下:
set -n ‘s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/’
由于sed默認只接受BRE,因此正則表達式中一些本作為元字符的字符會被當作文字字符。解決辦法是使用反斜線轉義會出問題的字符:
set -n ‘s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/’ distros.txt
aspell-交互式拼寫檢查器
aspell多為需要拼寫檢查功能的程序所用,但也可以作為獨立的命令行工具發揮效用。它能夠智能地檢查各種文本文件,其中包括HTML文檔、C/C++程序、電子郵件等。
命令格式如下:
aspell check textfile
例如檢查普通的文本文件:
foo.txt的文件內容:
The quick brown fox jimped over the laxy dog.
aspell check foo.txt
會提示jimped拼寫錯誤,會被高亮顯示,由于本人操作系統不支持英文拼寫檢查故不能進行測試。
對于HTML標簽內容拼寫有誤,加入檢查模式選項-H(HTML)就能解決這個問題:
aspell -H check foo.txt
不能測試原因同上。