Sed

sed（意為流編輯器，源自英語 “stream editor” 的縮寫）是一個使用簡單緊湊的程式語言來解析和轉換文字 Unix 實用程式。

sed 由貝爾實驗室的李・E・麥克馬洪於 1973 年至 1974 年開發，

並且現在大多數作業系統都可以使用。 sed 基於互動式編輯器 ed（“editor”，1971）和早期 qed（“quick editor”，1965-66）的指令碼功能。sed 是最早支援正則表示式的工具之一，至今仍然用於文字處理，特別是用於替換命令。用於純文字字串操作和 “流編輯” 的常用工具還有 AWK 和 Perl 。

歷史

sed 是為命令列處理資料檔案而構建的早期 Unix 命令之一，首次出現在 Version 7 Unix 中 。它很自然地演變成為流行的 grep 命令的後繼。 最初的動機與 grep（g/re/p）的替換類似，因此稱為 “g/re/s”。 考慮到這樣的話還會出現針對每個命令的專用程式，例如 g/re/d，李・E・麥克馬洪編寫了一個通用的面向行的流編輯器，該編輯器後來成為了 sed。 sed 的語法，特別是把 / 用於模式匹配，把 s/// 用於替換，起源於 sed 的前身 ed（當時 ed 很常用） 而且正則表示式語法影響了其他一些語言，特別是 ECMAScript 和 Perl。後來，更強大的語言 AWK 問世，這些工具相互補充，讓透過 shell 指令碼完成強大的文字處理成為可能。sed 和 AWK 常被認為 Perl 的祖先和靈感來源，並且影響了 Perl 的語法和語義，尤其影響了匹配和替換運算子。

GNU sed 添加了一些新功能，包括檔案的就地編輯 。Super-sed 是 sed 的擴充套件版本，包含與 Perl 相容的正則表示式。sed 的另一變體 minised ，最初埃裡克・雷蒙把 4.1BSD sed 透過逆向工程寫成，目前由 René Rebe 維護。在 GNU 計劃基於新的 GNU 正則表示式庫編寫了新版本的 sed 之前，GNU 計劃一直使用 minised。當前 minised 包含一些 BSD sed 的擴充套件，但不像 GNU sed 那樣功能豐富。它的優點是速度快，佔用的記憶體少。它用於嵌入式系統，是 Minix 提供的 sed 版本。

工作模式

sed 是一個面向行的文字處理實用程式：它從輸入流或檔案中逐行讀取文字到一個稱為模式空間 的內部緩衝區。每讀一行開始一個迴圈 。對於模式空間，sed 會應用 sed 指令碼 指定的一個或多個操作。sed 實現了一種程式語言，其中包含大約 25 個指定文字操作的命令 。對於每個輸入行，在執行指令碼之後，sed 通常輸出模式空間（由指令碼修改的行），然後從下一行再次開始迴圈。其他指令碼結束行為可透過 sed 選項和指令碼命令獲得，例如 d 刪除模式空間，q 退出，N 立即將下一行新增到模式空間，等等。因此，sed 指令碼對應於迴圈體，迴圈體遍歷流的行，其中迴圈本身和迴圈變數（當前行號）是隱式的並由 sed 維護。

可以在命令列上指定 sed 指令碼（ -e 選項），也可以從單獨的檔案中讀取（ -f 選項）。sed 指令碼中的命令可以採用行號或正則表示式的作為地址 。該地址確定決定命令何時執行。例如，2d 將僅在第二個輸入行上執行 d（刪除）命令（列印除第二個輸入行之外的所有行），而 /^ /d 將刪除以空格開頭的所有行。一些單獨的特殊緩衝區，即保持空間，可以由幾個 sed 命令使用，用於在迴圈之間保持和累積文字。sed 的命令語言只有兩個變數（“保持空間” 和 “模式空間”）和類似 GOTO 的分支功能；然而，這種語言是圖靈完備的， 用深奧 sed 指令碼甚至寫得出推箱子、打磚塊、國際象棋和俄羅斯方塊等遊戲。

為輸入流的每一行執行一次主迴圈，在輸入的每一行上計算 sed 指令碼。sed 指令碼的每一行都是模式 - 動作對，指示著要匹配的模式和要執行的操作，可以將其重新組合為條件語句。因為主迴圈、工作變數（模式空間和保持空間）、輸入和輸出流以及預設操作（複製行到模式空間、列印模式空間）是隱式的，所以可以編寫簡潔的單行程式。例如，以下 sed 程式：

10q

將列印輸入的前 10 行，然後停止。

用法

3.1 替換命令

下面的示例顯示了 sed 用於替換的典型（也是最常見的）用法。這種用法確實是 sed 的最初動機：

sed's/regexp/replacement/g' inputFileName > outputFileName

在某些版本的 sed 中，表示式的前面必須加上 - e，以表示後面跟著一個表示式。s 表示替換，而 g 表示全域性，這意味著行中的所有匹配項都將被替換。要搜尋的正則表示式（即 pattern）放在第一個分隔符號（此處為斜槓）之後，而要替換成的字串跟在第二個分隔符號後面。斜槓（/）是傳統的符號，起源於 ed 中的 “搜尋” 字元，但其實在 pattern 和替換文字中都未出現的任何其他符號都可以用作分隔符號，使其可讀性更強；這有助於避免 “傾斜牙籤綜合徵”。

替換命令源自 ed 中的搜尋 - 替換，實現了簡單的解析和模板化 。regexp 提供模式匹配和透過子表示式儲存文字的功能，而 replacement 可以是純文字，也可以是包含 “完全匹配”&，或第 n 個子表示式（從 \1 到 \9）這種特殊轉義序列的格式字串。例如， sed -r "s/(cat|dog) s?/\1s/g" 用 “cat” 或 “dog” 替換所有出現的 “cats” 或 “dogs”，且不重覆複製出原文內已有的 “s”：在正則表示式中，(cat|dog) 是第一個（也是唯一）儲存的子表示式，格式字串中的 \1 將其替換到輸出裡。

3.2 其他 sed 命令

除了替換之外，使用大約 25 個 sed 命令可以進行其他形式的簡單處理。例如，下面使用 d 命令刪除空行或只包含空格的行：

sed '/^ *$/d' inputFileName

本例使用了下列正則表示式元字元（sed 支援所有正則表示式）：

脫字元（ ^ ）匹配行首。
美元符號（ $ ）匹配行尾。
星號（ * ）匹配前一個字元零次或多次出現。

可以有很複雜的 sed 結構，讓 sed 用作一種簡單但高度專業化的程式語言。例如，可以透過使用標籤（冒號後跟字串）和分支指令 b 來管理控制流程。指令 b 後跟有效的標籤名稱，將把處理流程移動到該標籤後面的塊。

3.3 sed 用作過濾器

在 Unix 下，sed 通常用作管道中的過濾器：

$ generateData | sed's/x/y/g'

也就是說，諸如 “generateData” 之類的程式生成資料，然後用 sed 把 x 替換成 y。例如：

$ echo xyz xyz | sed's/x/y/g'

yyz yyz

3.4 基於檔案的 sed 指令碼

將幾個 sed 命令（每行一個命令）放入指令碼檔案（例如 subst.sed）中然後使用 - f 選項從檔案中執行命令（例如 s/x/y/g）通常很有用：

sed -f subst.sed inputFileName > outputFileName

可以在指令碼檔案中放置任意數量的命令，使用指令碼檔案也可以避免 shell 轉義或替換的問題。

這樣的指令碼檔案可以直接從命令列執行，方法是在其前面加上一個包含 sed 命令的 "shebang 行"，併為該檔案分配可執行許可權。例如，可以使用以下內容建立檔案 subst.sed：

!/bin/sed -f

s/x/y/g

然後，當前使用者可以使用 chmod 命令使檔案可執行：

chmod u+x subst.sed

然後可以直接從命令列執行該檔案：

subst.sed inputFileName > outputFileName

3.5 就地編輯

GNU sed 中引入的 - i 選項允許就地編輯檔案（實際上，在後臺建立了一個臨時輸出檔案，然後將原始檔案替換為臨時檔案）。例如：

sed -i's/abc/def/' fileName

示例

4.1 Hello, world! 例子

convert input text stream to "Hello, world!"

s/.*/Hello, world!/

q

這個 “Hello, world!” 指令碼位於檔案（如 script.txt）中，並使用 sed -f script.txt inputFileName 呼叫，其中 “inputFileName” 是輸入文字檔案。指令碼將 “inputFileName” 第 1 行更改為 “Hello, world!” 然後退出，在 sed 退出之前列印結果。第 1 行後的任何輸入行都不會被讀取，也不會被列印。唯一的輸出是 “Hello, world!”。

這個例子強調了 sed 的許多關鍵特性：

sed 是獨一無二的。沒有其他 “Hello, world!” 例子與之相似。
典型的 sed 程式相當簡短。
sed 指令碼可以有註釋（以 #符號開頭的行）。
s（替換）命令是最重要的 sed 命令。
sed 允許使用 q（退出）等命令進行簡單程式設計。
sed 使用正則表示式，例如.* （任何字元的零個或多個）。

4.2 其他簡單的例子

下面是各種 sed 指令碼；可以把它們作為引數傳遞給 sed，或者放入一個單獨的檔案並透過 - f 執行或透過使指令碼本身可執行來執行。

要把檔案中某個單詞（例如 IRC 密碼）替換為 “REDACTED”，並儲存結果：

sed -i s/yourpassword/REDACTED/./status.freenode.log

要刪除包含 “yourword” 一詞的所有行（ 地址 為 “/yourword/”）：

/yourword/d

要刪除所有 “yourword” 這個詞：

s/yourword//g

要同時從檔案中刪除兩個單詞：

s/firstword//g

s/secondword//g

為了在一行表示前面的示例，比如在命令列輸入時，可以透過分號連線兩個命令：

sed "s/firstword//g; s/secondword//g" inputFileName

4.3 多行處理示例

在下一個示例中，sed（通常僅在一行上工作）會在某一行的後一行以一個空格打頭的情況下刪除換行符。

請考慮以下文字：

This is my dog,

whose name is Frank.

This is my fish,

whose name is George.

This is my goat,

whose name is Adam.

下面的 sed 指令碼會將上面的文字轉換為以下文字。請注意，該指令碼僅影響以空格開頭的輸入行：

This is my dog, whose name is Frank.

This is my fish,

whose name is George.

This is my goat, whose name is Adam.

使用的指令碼是：

N

s/\n //

P

D

這段指令碼應按如下理解：

（N）將下一行新增到模式空間；
（s/\n //）查詢一個換行符後跟一個空格，替換為一個空格；
（P）列印模式空間的頂行；
（D）從模式空間中刪除頂行並再次執行指令碼。

這可以透過分號在一行中表示出來:

sed 'N; s/\n //; P; D' inputFileName

限制和替代方案

雖然 sed 具有簡單性和侷限性，但對於大量用途而言，它的功能已經足夠強大。對於更復雜的處理，可以使用更強大的語言，如 AWK 或 Perl 或者 PowerShell。雖然使用保持緩衝區理論上可以進行任意複雜的轉換，但如果轉換行的方式比正則表示式提取和模板替換更復雜，則使用一般會使用上面提到的更強大的語言。

相反，對於更簡單的操作，grep （列印匹配模式的行），head（列印檔案的第一部分），tail（列印檔案的最後部分）和 tr（翻譯或刪除字元）等專門的 Unix 實用程式通常更可取。對於設計用於執行的特定任務，此類專用實用程式通常比較一般的解決方案（如 sed）更簡單、清晰和快速。

ed/sed 命令和語法繼續用於派生程式，例如文字編輯器 vi 和 vim。sam/ssam 與 ed/sed 類似，其中 sam 是 Plan 9 編輯器，ssam 是它的流介面，其功能類似於 sed。