2007 年 2 月 16 日
簡(jiǎn)單研究一下可節(jié)省時(shí)間和精力的一些基本命令行文本編輯程序。文本編輯操作通常在文本編輯器應(yīng)用程序中交互式地進(jìn)行。然而,有些任務(wù)可以直接從 UNIX? 命令行方便快捷地完成。此外,還可以在腳本中使用這些單命令行程序來(lái)自動(dòng)化各種編輯過(guò)程。
大多數(shù) UNIX? 開(kāi)發(fā)人員都選擇 Emacs、vi 或這兩個(gè)文本編輯應(yīng)用程序的眾多變種、分支和克隆之一。操作員通常在所選的文本編輯器中打開(kāi)文件,并交互式地對(duì)文件指定和應(yīng)用更改。
但是與在文本編輯器中打開(kāi)文件相比,您通??梢栽诿钚懈斓赝瓿删庉嫻ぷ鳌?fù)雜的編輯過(guò)程可以從命令行進(jìn)行編程和指定,并跨多個(gè)文件執(zhí)行,從而消除所有不必要的屏幕顯示、光標(biāo)移動(dòng)和與文件的人工交互。一種很好的策略是在手邊保留一些相關(guān)的命令行程序,以完成常見(jiàn)的編輯工作。它們不僅可以為您節(jié)省時(shí)間(尤其是在涉及到多個(gè)文件的批處理操作中),而且您還可以在腳本中使用它們。
用于編輯和處理文本的單命令行程序是 Perl 和 AWK(以及最近的 Ruby)語(yǔ)言(當(dāng)然還包括 Shell)中有名的傳統(tǒng)功能。本文使用在所有系統(tǒng)上都隨時(shí)可用的三個(gè)最主要的命令行編輯工具來(lái)演示基本的文本編輯技術(shù):cat、ed 和 sed。下面的編輯示例首先從最簡(jiǎn)單和最常見(jiàn)的構(gòu)造開(kāi)始,并逐步過(guò)渡到較復(fù)雜的構(gòu)造。
使用 cat(其名稱表示“連接”)來(lái)連接文件和標(biāo)準(zhǔn)輸入流,如
清單 1 所示。世界上的懶鬼們還將它用作通用分頁(yè)程序 (cat file) 和完整的文本編輯環(huán)境 (cat > file)。其語(yǔ)法的簡(jiǎn)單性無(wú)與倫比,而且對(duì)于文本編輯單命令行程序,它還為您提供了無(wú)需編輯器即可追加或插入文本的快捷方法。
$ (cat - input1 - input2 - input3 - input4) | mailx ted Ted, Take a look at these example files. This is the first file ... Ctrl-D This is the second file ... Ctrl-D This is the third file -- note the fourth paragraph below ... Ctrl-D And here‘s the last file ... Ctrl-D $
然而,懶鬼也是講策略的。當(dāng)您需要將文本追加到文件結(jié)尾時(shí),再?zèng)]有比使用 cat 更快的方法了:
$ cat >> file > line > line > line
Ctrl-D $
當(dāng)您在添加行時(shí),按 Ctrl-U 可以刪除當(dāng)前行,按 Ctrl-Z 可以掛起該過(guò)程,按 Ctrl-C 可以中止所有操作。當(dāng)您完成編輯時(shí),可以在各行上按 Ctrl-D。(存在一些缺省的 Korn Shell 控制鍵,但它們適用于大多數(shù) Shell 和編輯模式。)
如果您正在輸入的數(shù)據(jù)是從另一個(gè)窗口粘貼而來(lái)的 X 選擇,則該單命令行程序通常更快速,因?yàn)槟槐卣{(diào)用某個(gè)編輯器、打開(kāi)目標(biāo)文件、移動(dòng)到文件末尾、粘貼選擇、保存文件然后再退出編輯器。當(dāng)您是在粘貼格式化或特殊格式化的文本,并且您希望保留該格式(因?yàn)槟承┪谋揪庉嬈骱途庉嬆J皆谀迟N X 選擇時(shí)會(huì)對(duì)其進(jìn)行重新格式化)時(shí),單命令行程序也會(huì)更有用。
雖然此操作非常常見(jiàn),是一項(xiàng)日常活動(dòng),但是您必須小心使用 shell 操作符來(lái)追加 重定向(>>) 而不是普通重定向操作符 (>);如果您錯(cuò)誤地使用了后者,則會(huì)使用原本打算追加的文本改寫文件的原有內(nèi)容。
若要將一個(gè)文件的全部?jī)?nèi)容追加到另一個(gè)文件結(jié)尾,您可以給出文件名:
$ cat footnotes.txt >> file
如果您僅追加單行而不是多行或整個(gè)文件,您可以使用 echo 而不是 cat:
$ echo "192.255.255.255 bigblue" >> /etc/hosts
若要追加從 1 開(kāi)始進(jìn)行項(xiàng)目編號(hào)的文本行,可以使用 cat 的 -n 選項(xiàng);這樣將在各行前面附加行號(hào)(最多偏移五個(gè)空格字符)和一個(gè)制表符。添加 -b 選項(xiàng)可以禁止對(duì)空白行編號(hào):
$ cat -nb > file This line is numbered And so is this Another numbered line Ctrl-D $ cat file 1 This line is numbered 2 And so is this 3 Another numbered line $
通過(guò)使用連字符 (-) 指定標(biāo)準(zhǔn)輸入并寫到一個(gè)新文件,您可以使用 cat 在文件開(kāi)頭插入文本:
$ cat - file > newfile This is the beginning of the file And then the old file is inserted Below this line: Ctrl-D $
雖然這個(gè)單命令行程序非常簡(jiǎn)單,但是它的缺點(diǎn)在于創(chuàng)建了一個(gè)新文件。如果您希望將文本插入原始文件,則必須進(jìn)行的重命名將使得此單命令行程序成事不足敗事有余。更好的方法是使用即將介紹的
ed。
cat 具有若干個(gè)有用的選項(xiàng)。其中一些選項(xiàng)控制它輸出非打印字符的方式,例如制表符和控制字符。若要確定某個(gè)文件或某一組文本文件是否有嵌入的控制字符,可以使用這些選項(xiàng)。例如,如果某個(gè)文件具有尾隨空格,您就可以使用這些選項(xiàng):
$ cat -vet input.txt This line has trailing blanks. $ This line does not.$ $
這些選項(xiàng)隨 UNIX 實(shí)現(xiàn)而異;
表 1 提供了標(biāo)準(zhǔn) IBM AIX? 操作系統(tǒng)的選項(xiàng)。
選項(xiàng) 描述
-b 不對(duì)空白行編號(hào)。
-e 使用 $ 字符顯示行尾。
-n 從 1 開(kāi)始對(duì)所有輸出行編號(hào)。
-q 使用靜默操作(禁止錯(cuò)誤消息)。
-r 將所有多個(gè)空行替換為單行(“壓縮”空白)。
-S 將多個(gè)空白行壓縮到單行中(與 -r 相同)。
-s 禁止錯(cuò)誤消息(靜默操作)。
-t 將制表符顯示為 ^I。
-u 不對(duì)輸出進(jìn)行緩沖。
-v 可視地顯示非打印控制字符。
顧名思義,行編輯器 ed 對(duì)輸入文件的行執(zhí)行編輯。它將整個(gè)文件讀入自己的緩沖區(qū),對(duì)該副本執(zhí)行指定的操作,并可選地將緩沖區(qū)寫到磁盤。您可以在編輯操作中指定任何數(shù)量的行,并且這些操作可以在一個(gè)序列中進(jìn)行組合和指定。這些事實(shí)使得 ed 成為在腳本中使用的理想選擇。以如下格式指定操作:
[address]command [text]
address 指定要處理的一行或多行(缺省為當(dāng)前行),并且可以通過(guò)多種方式進(jìn)行指定。單字符的 command 是要對(duì)指定行執(zhí)行的操作。對(duì)于腳本中的特別單命令行程序,可以使用 echo 將一組命令和文本管道傳輸給 ed,從而以非交互式的方式使用它。
( echo ‘OPERATION‘; echo ‘OPERATION‘; ... echo ‘wq‘ ) | ed -s FILENAME
如果在操作中輸入文本,應(yīng)該回顯一個(gè)句點(diǎn) (.) 來(lái)指示輸入結(jié)束。最后的 wq 寫入文件并退出。-s 選項(xiàng)使 ed 靜默地操作,并禁止所有正常輸出。
幸運(yùn)的是,ed 的基本尋址方法和命令是相當(dāng)標(biāo)準(zhǔn)化的。
表 2 描述了主要的尋址形式。
表 3 給出了命令。
選項(xiàng) 描述
. 此選項(xiàng)對(duì)當(dāng)前行尋址(缺省地址)。
number 此選項(xiàng)對(duì)第 number 行尋址??梢园炊禾?hào)分隔的范圍 (first,last) 對(duì)行尋址。0 代表緩沖區(qū)的開(kāi)頭(第一行之前)。
-number 此選項(xiàng)對(duì)當(dāng)前行之前的第 number 行尋址。如果沒(méi)有 number,則減號(hào)對(duì)緊跟在當(dāng)前行之前的行尋址。
+number 此選項(xiàng)對(duì)當(dāng)前行之后的第 number 行尋址。如果沒(méi)有 number,則加號(hào)對(duì)緊跟在當(dāng)前行之后的行尋址。
$ 此選項(xiàng)對(duì)最后一行尋址。
, 此選項(xiàng)對(duì)第一至最后一行尋址,包括第一行和最后一行(與 1,$ 相同)。
; 此選項(xiàng)對(duì)當(dāng)前行至最后一行尋址。
/pattern/ 此選項(xiàng)對(duì)下一個(gè)包含與 pattern 匹配的文本的行尋址。
pattern 此選項(xiàng)對(duì)上一個(gè)包含與 pattern 匹配的文本的行尋址。
命令 描述
a 此命令在指定的地址之后追加文本。
c 此命令將指定的地址更改為給定的文本。
d 此命令刪除指定地址處的行。
i 此命令在指定的地址之前插入文本。
q 此命令在將緩沖區(qū)保存到磁盤后終止程序并退出。
r file 此命令讀取 filespec 的內(nèi)容并將其插入指定的地址之后。
s/pattern/replacement/ 此命令將匹配 pattern 的文本替換為指定地址中的 replacement 文本。
w file 此命令將指定的地址寫到 file。如果沒(méi)有 address,則此命令缺省使用整個(gè)緩沖區(qū)。
通過(guò)可在腳本中使用的 ed 單命令行程序,您可以容易地在文件開(kāi)頭插入文本。插入操作是使用 ed 并通過(guò) a 命令將給定文本追加到第 0 行(文件開(kāi)頭)來(lái)完成的:
$ cat file This is the end. $ (echo ‘0a‘; echo ‘This is the beginning.‘; echo ‘.‘; echo ‘wq‘) | ed -s file $ cat file This is the beginning. This is the end. $
您可以交互式地完成同樣的任務(wù):
$ cat file This is the end. $ ed -s file > 0a > This is the beginning. > . > wq $ cat file This is the beginning. This is the end. $
若要在文件開(kāi)頭插入另一個(gè)文件的內(nèi)容,可以使用 r 命令:
$ (echo ‘0r headnotes‘; echo ‘wq‘) | ed file
您可以使用 ed 將任何數(shù)量的文本行插入文件中任意行之前或之后。若要在第一個(gè)包含給定字符串的行之后插入,可以將該字符串包括在斜杠中,并在后面跟著 a 命令以追加隨后的文本。與前面一樣,各個(gè)行使用一個(gè)句點(diǎn)結(jié)束,并使用 wq 寫入文件并退出。
當(dāng)您希望在文件中的特定位置追加文本塊時(shí),此項(xiàng)技術(shù)就會(huì)派上用場(chǎng):
$ ( echo ‘/begin/a‘; echo ‘This is the middle.‘; \ > echo ‘.‘; echo ‘wq‘) | ed -s file $ cat file This is the beginning. This is the middle. This is the end. $
當(dāng)您對(duì)一組文件執(zhí)行多行文本插入時(shí),此項(xiàng)技術(shù)也非常有用。如果要插入大量的行,可以使用 here document,這是使用 << 和一個(gè)限制字符串以內(nèi)聯(lián)方式指定的文檔,用于重定向其后直至到達(dá)限制字符串的所有輸入(請(qǐng)參見(jiàn)
參考資料):
$ for i in *.xml > { ed -s $i << EOF > /<records>/a \ > <record> \ > <name>johnnycomelately</name> \ > <step>10</step> \ > <dur>4</dur> \ > </record>\ > . > wq > EOF > } $
您可以在給定字符串之后插入一個(gè)文件:
$ (echo ‘/END OF PART I/r footnotes.txt‘; echo ‘wq‘) | ed file
使用 d 命令來(lái)刪除文件中的行。與本文討論的所有命令一樣,您可以指定任何類型的
有效地址,例如特定的行或行范圍。在實(shí)踐中,此單命令行程序最適合于與至少一個(gè)匹配的模式結(jié)合使用,例如刪除從第一個(gè)匹配某模式的行到文件結(jié)尾的所有行:
$ ( echo ‘/FOOTNOTES/,$/d‘; echo ‘wq‘ ) | ed -s file
也可以按相反方向執(zhí)行此操作,并刪除從該文件的第一行到第一個(gè)匹配某模式的行的所有內(nèi)容:
$ ( echo ‘1,/\.\.\./d‘; echo ‘wq‘ ) | ed -s file
通過(guò)使用 s 命令并替換一個(gè)空替換字符,您可以刪除尾隨空格:
$ cat -vet input.txt This line has trailing blanks. $ This line does not.$ $ (echo ‘,s/ *$//‘; echo ‘wq‘) | ed -s input.txt $ cat -vet input.txt This line has trailing blanks.$ This line does not.$ $
回頁(yè)首本文討論的最復(fù)雜和最強(qiáng)大的編輯工具是 sed(流編輯器)。它是一個(gè)文本編輯器,但是與諸如 ed 等文本編輯器不同,它編輯輸入流并寫到輸出流。因此,它對(duì)于編輯命令輸出或?qū)τ谑褂闷渌ぞ邔?duì)文件進(jìn)行預(yù)處理非常有用——然后您可以將該文本通過(guò)管道直接輸出給 sed,以進(jìn)行快速編輯。但是 sed 還可以操作文件,并且其腳本語(yǔ)言具有高級(jí)模式匹配功能,因此它是用于執(zhí)行任何類型的快速文本編輯的理想選擇——例如對(duì)一組文件進(jìn)行快速搜索和替換。事實(shí)上,它是現(xiàn)有用于文本編輯的最流行命令行工具之一。
sed 接受包含任何數(shù)量命令的腳本,后面跟著可選的指定輸入文件的選項(xiàng);缺省情況下,它讀取標(biāo)準(zhǔn)輸入。某些版本的 sed 有一個(gè) -i 選項(xiàng),此選項(xiàng)指定應(yīng)該編輯的輸入文件。(如果沒(méi)有此選項(xiàng),則讀取輸入文件,而不對(duì)其執(zhí)行寫入。)如果您安裝的版本支持此選項(xiàng),則應(yīng)該使用它——它允許您使用單個(gè)命令對(duì)任何指定的文件執(zhí)行快速編輯操作。
sed -i script filespec
以下示例假設(shè)您的 sed 支持 -i 選項(xiàng)。否則,您必須使用 Shell 重定向?qū)⑤敵霰4娴叫挛募?,并在另一個(gè)步驟將新文件重命名為舊文件,從而執(zhí)行臨時(shí)文件中轉(zhuǎn):
sed script file > newfile; mv newfile file
對(duì)于多個(gè)文件,您必須執(zhí)行循環(huán):
for i in *; { sed script $i > $i.new; mv $i.new $i; }
您可以使用 s/searchstring/replacestring/ 構(gòu)造將給定字符串替換為另一個(gè)字符串。若要替換某個(gè)文件中每行上的第一個(gè) old 實(shí)例,可以使用以下命令:
$ sed -i ‘s/old/new/‘ file
若要替換每個(gè)實(shí)例,可以對(duì)該搜索追加 g 選項(xiàng)。此項(xiàng)技術(shù)對(duì)于修復(fù)輸入錯(cuò)誤或替換一個(gè)或一組文件中的重復(fù)單詞、短語(yǔ)或其他內(nèi)容非常理想。
$ sed -i ‘s/Esclipse/Eclipse/g‘ *.xml
您可以在輸入表達(dá)式中將字符包括在方括號(hào)中,但是,如果您在替換文本中使用方括號(hào),則會(huì)將它們視為普通字符:
$ cat file This is the beginning. This is the middle. This is the end. $ sed ‘s/[Tt]h/[Tt]h/g‘ file [Tt]his is [Tt]he beginning. [Tt]his is [Tt]he middle. [Tt]his is [Tt]he end.
當(dāng)要搜索或替換的短語(yǔ)包括斜杠字符時(shí),應(yīng)使用它來(lái)定義新的分隔符:
$ sed -i ‘s,/usr/local/websphere,/usr/websphere,‘ file
您還可以將包含某個(gè)模式的整個(gè)行替換為某些新文本:
$ sed -i ‘s/.*pattern.*/LINE DELETED/‘ file
回想一下在模式中將字符分組在一起的方括號(hào)示例,以及如何在替換文本中將它們視為普通字符。如果您希望在替換文本中包括字面匹配的模式,該怎么辦呢?可以使用“和”號(hào) (&) 來(lái)實(shí)現(xiàn)。此方法對(duì)于通過(guò)在匹配模式之前或之后放置文本來(lái)編輯匹配模式是非常有用的:
$ cat file This is the beginning. This is the middle. This is the end. $ sed ‘s/[Tt]h/>&</g‘ file >Th<is is >th<e beginning. >Th<is is >th<e middle. >Th<is is >th<e end.
使用 a 命令在給定的匹配模式后面添加一行文本:
$ sed -i ‘/pattern/a text‘ file
這并不替換與模式匹配的文本——它只是在第一個(gè)包含該模式的行后面添加文本。
若要在每行開(kāi)頭插入文本,可以匹配脫字號(hào)元字符并提供要插入的文本。下面顯示了如何向文件中的所有行添加電子郵件樣式引用:
$ sed ‘s/^/> /‘ input.txt > This line has trailing blanks. > This line does not. $
同樣的原理也適用于在每行結(jié)尾插入文本——匹配美元符號(hào)元字符并提供要插入的文本。下面演示了如何模擬 AIX cat 的 -vet 選項(xiàng)來(lái)標(biāo)記尾隨空格:
$ sed ‘s/$/$/‘ file This line has trailing blanks. $ This line does not.$ $
d 命令刪除給定的行。您可以在它前面附加行號(hào)、范圍、要匹配或包括在斜杠中的模式。
若要?jiǎng)h除文件中的第一行,可以使用以下命令:
$ sed -i 1d file
若要?jiǎng)h除第 1 至第 10 行,可以使用以下命令:
$ sed -i 1,10d file
若要?jiǎng)h除“BEGIN QUOTE”字符串的第一個(gè)實(shí)例到“END QUOTE”字符串的第一個(gè)實(shí)例之間的所有行,可以使用以下命令:
$ sed -i ‘/BEGIN QUOTE/,/END QUOTE/d‘ file
若要?jiǎng)h除當(dāng)前目錄中擴(kuò)展名為 .xml 的所有文件中第一行包含“<record>”并且最后一行包含“"</record>"”的所有文本部分,可以使用以下命令:
$ sed -i ‘/<record>/,/<\/record>/d‘ *.xml
若要?jiǎng)h除從第一行直到第一個(gè)空白行的所有內(nèi)容,可以使用以下命令:
$ sed -i ‘/^> /d‘ file
(當(dāng)在電子郵件消息或 Usenet 文章中使用時(shí),前述單命令行程序?qū)⒊ニ袠?biāo)頭。)
若要?jiǎng)h除所有以電子郵件樣式引用開(kāi)頭的行,可以使用以下命令:
$ sed -i /^$/d file
若要?jiǎng)h除文件的最后一行,可以使用以下命令:
$ sed -i ‘$d‘ file
如果文件中的行包含需要清除的尾隨空格字符,在文本編輯器中人工查找并刪除它們會(huì)非常麻煩,但是使用 sed 完成此任務(wù)將成為一個(gè)快速的單行操作。您可以搜索行尾之前出現(xiàn)一次或多次的字面空格字符,并將其替換為空字符:
$ cat -vet input.txt This line has trailing blanks. $ This line does not.$ $ sed -i ‘s/ *$//‘ input.txt $ cat -vet input.txt This line has trailing blanks.$ This line does not.$ $
回頁(yè)首通過(guò)從 UNIX 命令行運(yùn)行單命令行程序,可以利用多種有意義和復(fù)合的方式(無(wú)需編輯器)對(duì)文本文件進(jìn)行編輯。您這樣做有許多很好的理由:為了提高速度和方便性,在無(wú)法或不適合使用交互式編輯的情況下可編寫腳本,有時(shí)為了對(duì)單個(gè)文件或一組文件執(zhí)行復(fù)雜編輯,這些編輯操作難于甚至無(wú)法在交互式應(yīng)用程序中完成。本文使用三個(gè)普遍存在的編輯工具,通過(guò)許多簡(jiǎn)單文本編輯單命令行程序闡述了這一概念:cat、ed 和 sed。
學(xué)習(xí)
您可以參閱本文在 developerWorks 全球網(wǎng)站上的
英文原文。
有關(guān)以下工具的 AIX 5L Version 5.3 版本的信息,請(qǐng)參考手冊(cè)頁(yè):
catedsed“
UNIX 中的文本處理”(developerWorks,2006 年 8 月):這篇文章介紹了如何結(jié)合使用 sed 和其他命令行工具來(lái)執(zhí)行強(qiáng)大的文本處理操作。
“
對(duì)話 UNIX,第 1 部分:掌握強(qiáng)大的命令行”(developerWorks,2006 年 3 月):了解 Shell 中的重定向和流如何工作。
Teodor Zlatanov 的 Cultured Perl 專欄展示了 Perl 單命令行程序。在“
One-liners 101”(developerWorks,,2001 年 4 月)和“
One-liners 102”(developerWorks,2003 年 3 月)中閱讀關(guān)于它們的信息。
IBM System p? and AIX 信息中心:訪問(wèn)此站點(diǎn)以獲得關(guān)于 AIX and System p 的文檔。
AIX and UNIX articles:查看 Michael Stutz 撰寫的其他文章。
按主題搜索“AIX and UNIX”庫(kù):
系統(tǒng)管理應(yīng)用程序開(kāi)發(fā)性能移植安全性提示工具和實(shí)用程序Java? technologyLinux?開(kāi)放源代碼AIX and UNIX:“AIX and UNIX developerWorks”專區(qū)提供了大量與 AIX 系統(tǒng)管理的所有方面相關(guān)并擴(kuò)展您的 UNIX 技能的信息。
New to AIX and UNIX:訪問(wèn)“New to AIX and UNIX”頁(yè)面以了解更多關(guān)于 AIX 和 UNIX 的內(nèi)容。
AIX 5L? Wiki:AIX 相關(guān)技術(shù)信息的協(xié)作環(huán)境。
Safari 書(shū)店:訪問(wèn)這個(gè)電子參考庫(kù)以查找特定的技術(shù)資源。
developerWorks 技術(shù)事件和網(wǎng)絡(luò)廣播:了解最新的 developerWorks 技術(shù)事件和網(wǎng)絡(luò)廣播。
Podcasts:收聽(tīng) Podcast 并與 IBM 技術(shù)專家保持同步。
獲得產(chǎn)品和技術(shù)
IBM 試用軟件:使用 IBM 試用軟件開(kāi)發(fā)您的下一個(gè)項(xiàng)目,可直接從 developerWorks 下載這些試用軟件。
討論
參與
developerWorks blogs,從而加入到 developerWorks 社區(qū)中來(lái)。
參與“AIX and UNIX”論壇:
AIX 5L——技術(shù)論壇AIX for Developers 論壇集群系統(tǒng)管理IBM Support Assistant 性能工具——技術(shù)虛擬化——技術(shù)更多“AIX and UNIX”論壇Michael Stutz 是
The Linux Cookbook 一書(shū)的作者,他僅使用開(kāi)放源碼軟件對(duì)該書(shū)進(jìn)行了設(shè)計(jì)和排版。他的研究興趣包括數(shù)字出版和圖書(shū)的發(fā)展未來(lái)。他使用各種 UNIX 操作系統(tǒng)已有 20 多年。您可以通過(guò)
stutz@dsl.org 與他聯(lián)系。