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行，然後停止。

## 用法

### 替換命令

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

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

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

替換命令源自ed中的搜索-替換，實現了簡單的解析和模板化。`regexp`提供模式匹配和通過子表達式保存文本的功能，而`replacement`可以是純文本，也可以是包含「完全匹配」`&amp;`，或第n 個子表達式（從`\1`到`\9`）這種特殊轉義序列的格式字串。例如。

```bash
sed -r "s/(cat|dog)s?/\1s/g"
```

用「cat」或「dog」替換所有出現的「cats」或「dogs」，且不重覆複製出原文內已有的「s」：在正則表達式中，`(cat|dog)`是第一個（也是唯一）保存的子表達式，格式字串中的`\1`將其替換到輸出里。

### 其他sed命令

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

```bash
sed '/^ *$/d' inputFileName
```

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

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

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

### sed用作過濾器

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

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

也就是說，諸如「generateData」之類的程序生成數據，然後用sed把x替換成y。例如：

```console
$ echo xyz xyz | sed 's/x/y/g'
yyz yyz
```

### 基於文件的sed腳本

將幾個sed命令（每行一個命令）放入腳本文件（例如`subst.sed`）中然後使用`-f`選項從文件中運行命令（例如`s/x/y/g`）通常很有用：

```bash
sed -f subst.sed inputFileName > outputFileName
```

可以在腳本文件中放置任意數量的命令，使用腳本文件也可以避免shell轉義或替換的問題。

這樣的腳本文件可以直接從命令行執行，方法是在其前面加上一個包含sed命令的"shebang行"，並為該文件分配可執行權限。例如，可以使用以下內容創建文件`subst.sed`：

```bash
#!/bin/sed -f
s/x/y/g
```

然後，當前使用者可以使用`chmod`命令使文件可執行：

```bash
chmod u+x subst.sed
```

然後可以直接從命令行執行該文件：

```bash
subst.sed inputFileName > outputFileName
```

### 就地編輯

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

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

## 示例

### Hello, world! 例子

```console
# 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使用正則表達式，例如`.*` （任何字符的零個或多個）。

### 其他簡單的例子

下面是各種sed腳本；可以把它們作為參數傳遞給sed，或者放入一個單獨的文件並通過`-f`執行或通過使腳本本身可執行來執行。

要把文件中某個單詞（例如IRC密碼）替換為「REDACTED」，並保存結果：

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

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

```bash
/yourword/ d
```

要刪除所有「yourword」這個詞：

```bash
s/yourword//g
```

要同時從文件中刪除兩個單詞：

```bash
s/firstword//g
s/secondword//g
```

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

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

### 多行處理示例

在下一個示例中，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。

## 擴展閱讀

- 貝爾實驗室的第八版（約1985年）Unix sed（1）手冊頁
- GNU sed（1）手冊頁
- Sourceforge.net，sed FAQ（2003年3月）

## 外部連結

### 教程

- Sed - An Introduction and Tutorial, by Bruce Barnett
- SED -- A Non-interactive Text Editor (1974), by Lee E. McMahon
- 31+ Examples For Sed Linux Command In Text Manipulation, by Mokhtar Ebrahim
- Sed教學

### 示例

- Major sources for sed scripts, files, usage
- Roger Chang's SED and Shell Scripts (2012)
- Top 'sed' commands – Usage examples
- Sed command examples in Unix & Linux

### 其他連結

- //www.gnu.org/software/sed/ GNU sed主頁（包括GNU sed手冊）
- sed the Stream Editor（2004） （Eric Pement）
- sed-users Yahoo討論組

## 基本資料

- 日語原文：sed
- 編程范型：腳本
- 發行日期：1974年
- 設計者：李·E·麥克馬洪
- 實作語言：C
- 受影響於：ed
- 施影響於：Chomski, Perl, AWK
- screenshot caption：GNU sed 使用手冊的節錄