sed 是十分強大和小巧的文本流編輯器。在本文章系列的第二篇中,Daniel Robbins" name="description" />
![]() | sed 是十分強大和小巧的文本流編輯器。在本文章系列的第二篇中,Daniel Robbins 為您演示如何使用 sed 來執行字符串替換、創建更大的 sed 腳本以及如何使用 sed 的附加、插入和更改行命令。 sed 是很有用(但常被遺忘)的 UNIX 流編輯器。在以批處理方式編輯文件或以有效方式創建 shell 腳本來修改現有文件方面,它是十分理想的工具。本文是前一篇介紹 sed 文章的續篇。 替換! $ sed -e 's/foo/bar/' myfile.txt 上面的命令將 myfile.txt 中每行第一次出現的 'foo'(如果有的話)用字符串 'bar' 替換,然后將該文件內容輸出到標準輸出。請注意,我說的是每行第一次出現,盡管這通常不是您想要的。在進行字符串替換時,通常想執行全局替換。也就是說,要替換每行中的所有出現,如下所示: $ sed -e 's/foo/bar/g' myfile.txt 在最后一個斜杠之后附加的 'g' 選項告訴 sed 執行全局替換。 關于 's///' 替換命令,還有其它幾件要了解的事。首先,它是一個命令,并且只是一個命令,在所有上例中都沒有指定地址。這意味著,'s///' 還可以與地址一起使用來控制要將命令應用到哪些行,如下所示: $ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt 上例將導致用短語 'entrapment' 替換所有出現的短語 'enchantment',但是只在第一到第十行(包括這兩行)上這樣做。 $ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt 該例將用 'mountains' 替換 'hills',但是,只從空行開始,到以三個字符 'END' 開始的行結束(包括這兩行)的文本塊上這樣做。 關于 's///' 命令的另一個妙處是 '/' 分隔符有許多替換選項。如果正在執行字符串替換,并且規則表達式或替換字符串中有許多斜杠,則可以通過在 's' 之后指定一個不同的字符來更改分隔符。例如,下例將把所有出現的 /usr/local 替換成 /usr: $ sed -e 's:/usr/local:/usr:g' mylist.txt 在該例中,使用冒號作為分隔符。如果需要在規則表達式中指定分隔符字符,可以在它前面加入反斜杠。 規則表達式混亂 $ sed -e 's/<.*>//g' myfile.html 這是要從文件除去 HTML 標記的第一個很好的 sed 腳本嘗試,但是由于規則表達式的特有規則,它不會很好地工作。原因何在?當 sed 試圖在行中匹配規則表達式時,它要在行中查找最長的匹配。在我的 前一篇 sed 文章中,這不成問題,因為我們使用的是 'd' 和 'p' 命令,這些命令總要刪除或打印整行。但是,在使用 's///' 命令時,確實有很大不同,因為規則表達式匹配的整個部分將被目標字符串替換,或者,在本例中,被刪除。這意味著,上例將把下行:<b>This</b> is what <b>I</b> meant. 變成: meant. 我們要的不是這個,而是: This is what I meant. 幸運的是,有一種簡便方法來糾正該問題。我們不輸入“'<' 字符后面跟有一些字符并以 '>' 字符結束”的規則表達式,而只需輸入一個“'<' 字符后面跟有任意數量非 '>' 字符并以 '>' 字符結束”的規則表達式。這將與最短、而不是最長的可能性匹配。新命令如下: $ sed -e 's/<[^>]*>//g' myfile.html 在上例中,'[^>]' 指定“非 '>'”字符,其后的 '*' 完成該表達式以表示“零或多個非 '>' 字符”。對幾個 html 文件測試該命令,將它們管道輸出到 "more",然后仔細查看其結果。 更多字符匹配 '[a-x]*' 這將匹配零或多個全部為 'a'、'b'、'c'...'v'、'w'、'x' 的字符。另外,可以使用 '[:space:]' 字符類來匹配空格。以下是可用字符類的相當完整的列表:
盡可能使用字符類是很有利的,因為它們可以更好地適應非英語 locale(包括某些必需的重音字符等等). 高級替換功能 $ sed -e 's/.*/ralph said: &/' origmsg.txt 輸出如下: ralph said: Hiya Jim, ralph said: ralph said: 該例的替換字符串中使用了 '&' 字符,該字符告訴 sed 插入整個匹配的規則表達式。因此,可以將與 '.*' 匹配的任何內容(行中的零或多個字符的最大組或整行)插入到替換字符串中的任何位置,甚至多次插入。這非常好,但 sed 甚至更強大。 那些極好的帶反斜杠的圓括號 foo bar oni eeny meeny miny larry curly moe jimmy the weasel 現在假設要編寫一個 sed 腳本,該腳本將把 "eeny meeny miny" 替換成 "Victor eeny-meeny Von miny" 等等。要這樣做,首先要編寫一個由空格分隔并與三個字符串匹配的規則表達式。 '.* .* .*' 現在,將在其中每個感興趣的區域兩邊插入帶反斜杠的圓括號來定義區域: '(.*) (.*) (.*)' 除了要定義三個可在替換字符串中引用的邏輯區域以外,該規則表達式的工作原理將與第一個規則表達式相同。下面是最終腳本: $ sed -e 's/(.*) (.*) (.*)/Victor 1-2 Von 3/' myfile.txt 如您所見,通過輸入 'x'(其中,x 是從 1 開始的區域號)來引用每個由圓括號定界的區域。輸入如下: Victor foo-bar Von oni Victor eeny-meeny Von miny Victor larry-curly Von moe Victor jimmy-the Von weasel 隨著對 sed 越來越熟悉,您可以花最小力氣來進行相當強大的文本處理。您可能想如何使用熟悉的腳本語言來處理這種問題 -- 能用一行代碼輕易實現這樣的解決方案嗎? 組合使用 $ sed -n -e '=;p' myfile.txt 無論什么時候指定了兩個或更多命令,都按順序將每個命令應用到文件的每一行。在上例中,首先將 '=' 命令應用到第 1 行,然后應用 'p' 命令。接著,sed 繼續處理第 2 行,并重復該過程。雖然分號很方便,但是在某些場合下,它不能正常工作。另一種替換方法是使用兩個 -e 選項來指定兩個不同的命令: $ sed -n -e '=' -e 'p' myfile.txt 然而,在使用更為復雜的附加和插入命令時,甚至多個 '-e' 選項也不能幫我們的忙。對于復雜的多行腳本,最好的方法是將命令放入一個單獨的文件中。然后,用 -f 選項引用該腳本文件: $ sed -n -f mycommands.sed myfile.txt 這種方法雖然可能不太方便,但總是管用。 一個地址的多個命令 1,20{ s/[Ll]inux/GNU/Linux/g s/samba/Samba/g s/posix/POSIX/g } 上例將把三個替換命令應用到第 1 行到第 20 行(包括這兩行)。還可以使用規則表達式地址或者二者的組合: 1,/^END/{ s/[Ll]inux/GNU/Linux/g s/samba/Samba/g s/posix/POSIX/g p } 該例將把 '{ }' 之間的所有命令應用到從第 1 行開始,到以字母 "END" 開始的行結束(如果在源文件中沒發現 "END",則到文件結束)的所有行。 附加、插入和更改行 i This line will be inserted before each line 如果不為該命令指定地址,那么它將應用到每一行,并產生如下的輸出: This line will be inserted before each line line 1 here 如果要在當前行之前插入多行,可以通過在前一行之后附加一個反斜杠來添加附加行,如下所示: i insert this line and this one and this one and, uh, this one too. 附加命令的用法與之類似,但是它將把一行或多行插入到模式空間中的當前行之后。其用法如下: a insert this line after each line. Thanks! :) 另一方面,“更改行”命令將實際替換模式空間中的當前行,其用法如下: c You're history, original line! Muhahaha! 因為附加、插入和更改行命令需要在多行輸入,所以將把它們輸入到一個文本 sed 腳本中,然后通過使用 '-f' 選項告訴 sed 執行它們。使用其它方法將命令傳遞給 sed 會出現問題。 下一篇 參考資料
關于作者 | ![]() |