---簡介 html
sed 是十分強大和小巧的文本流編輯器。在本文章系列的第二篇中,Daniel Robbins 爲您演示如何使用 sed 來執行字符串替換、建立更大的 sed 腳本以及如何使用 sed 的附加、插入和更改行命令。 git
sed 是頗有用(但常被遺忘)的 UNIX 流編輯器。在以批處理方式編輯文件或以有效方式建立 shell 腳原本修改現有文件方面,它是十分理想的工具。本文是 前一篇介紹 sed 文章的續篇。 shell
---替換! curl
讓咱們看一下 sed 最有用的命令之一,替換命令。使用該命令,能夠將特定字符串或匹配的規則表達式用另外一個字符串替換。下面是該命令最基本用法的示例: 編輯器
$ sed -e 's/foo/bar/' myfile.txt
上面的命令將 myfile.txt 中每行第一次出現的 'foo'(若是有的話)用字符串 'bar' 替換,而後將該文件內容輸出到標準輸出。請注意,我說的是 每行第一次出現,儘管這一般不是您想要的。在進行字符串替換時,一般想執行全局替換。也就是說,要替換每行中的 全部出現,以下所示: 工具
$ sed -e 's/foo/bar/g' myfile.txt
在最後一個斜槓以後附加的 'g' 選項告訴 sed 執行全局替換。 測試
關於 's///' 替換命令,還有其它幾件要了解的事。首先,它是一個命令,而且只是一個命令,在全部上例中都沒有指定地址。這意味着,'s///' 還能夠與地址一塊兒使用來控制要將命令應用到哪些行,以下所示: this
$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt上例將致使用短語 'entrapment' 替換全部出現的短語 'enchantment',可是隻在第一到第十行(包括這兩行)上這樣作。
$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt
該例將用 'mountains' 替換 'hills',可是,只從空行開始,到以三個字符 'END' 開始的行結束(包括這兩行)的文本塊上這樣作。 url
關於 's///' 命令的另外一個妙處是 '/' 分隔符有許多替換選項。若是正在執行字符串替換,而且規則表達式或替換字符串中有許多斜槓,則能夠經過在 's' 以後指定一個不一樣的字符來更改分隔符。例如,下例將把全部出現的 /usr/local 替換成 /usr: spa
$ sed -e 's:/usr/local:/usr:g' mylist.txt在該例中,使用冒號做爲分隔符。若是須要在規則表達式中指定分隔符字符,能夠在它前面加入反斜槓。
---規則表達式混亂
目前爲止,咱們只執行了簡單的字符串替換。雖然這很方便,可是咱們還能夠匹配規則表達式。例如,如下 sed 命令將匹配從 '<' 開始、到 '>' 結束、而且在其中包含任意數量字符的短語。下例將刪除該短語(用空字符串替換):
$ 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:]' 字符類來匹配空格。如下是可用字符類的至關完整的列表:
字符類 | 描述 |
[:alnum:] | 字母數字 [a-z A-Z 0-9] |
[:alpha:] | 字母 [a-z A-Z] |
[:blank:] | 空格或製表鍵 |
[:cntrl:] | 任何控制字符 |
[:digit:] | 數字 [0-9] |
[:graph:] | 任何可視字符(無空格) |
[:lower:] | 小寫 [a-z] |
[:print:] | 非控制字符 |
[:punct:] | 標點字符 |
[:space:] | 空格 |
[:upper:] | 大寫 [A-Z] |
[:xdigit:] | 十六進制數字 [0-9 a-f A-F] |
儘量使用字符類是頗有利的,由於它們能夠更好地適應非英語 locale(包括某些必需的重音字符等等).
---高級替換功能
咱們已經看到如何執行簡單甚至有些複雜的直接替換,可是 sed 還能夠作更多的事。實際上能夠引用匹配規則表達式的部分或所有,並使用這些部分來構造替換字符串。做爲示例,假設您正在回覆一條消息。下例將在每一行前面加上短語 "ralph said: ":
$ sed -e 's/.*/ralph said: &/' origmsg.txt |
輸出以下:
ralph said: Hiya Jim, ralph said: ralph said: I sure like this sed stuff! ralph said: |
該例的替換字符串中使用了 '&' 字符,該字符告訴 sed 插入整個匹配的規則表達式。所以,能夠將與 '.*' 匹配的任何內容(行中的零或多個字符的最大組或整行)插入到替換字符串中的任何位置,甚至屢次插入。這很是好,但 sed 甚至更強大。
---那些極好的帶反斜槓的圓括號
's///' 命令甚至比 '&' 更好,它容許咱們在規則表達式中定義 區域,而後能夠在替換字符串中引用這些特定區域。做爲示例,假設有一個包含如下文本的文件:
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 腳本時,須要有輸入多個命令的能力。有幾種方法這樣作。首先,能夠在命令之間使用分號。例如,如下命令系列使用 '=' 命令和 'p' 命令,'=' 命令告訴 sed 打印行號,'p' 命令明確告訴 sed 打印該行(由於處於 '-n' 模式)。
$ 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 |
這種方法雖然可能不太方便,但老是管用。
---一個地址的多個命令
有時,可能要指定應用到一個地址的多個命令。這在執行許多 's///' 以變換源文件中的字和語法時特別方便。要對一個地址執行多個命令,可在文件中輸入 sed 命令,而後使用 '{ }' 字符將這些命令分組,以下所示:
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",則到文件結束)的全部行。
---附加、插入和更改行
既然在單獨的文件中編寫 sed 腳本,咱們能夠利用附加、插入和更改行命令。這些命令將在當前行以後插入一行,在當前行以前插入一行,或者替換模式空間中的當前行。它們也能夠用來將多行插入到輸出。插入行命令用法以下:
i\ This line will be inserted before each line |
若是不爲該命令指定地址,那麼它將應用到每一行,併產生以下的輸出:
This line will be inserted before each line line 1 here This line will be inserted before each line line 2 here This line will be inserted before each line line 3 here This line will be inserted before each line line 4 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 會出現問題。
---下一篇
在下一篇、也是本 sed 系列的最後一篇文章中,我將爲您演示許多使用 sed 來完成不一樣類型任務的極佳實例。我將不只爲您顯示腳本作些什麼,還顯示 爲何那樣作。完成以後,您將掌握更多有關如何在不一樣項目中使用 sed 的極佳知識。到時候見!