ANSI转义序列

**ANSI 跳脫序列（ANSI escape sequences）** 是一種帶內訊號的跳脫序列標準，用於控制影片文字終端上的游標位置、顏色和其他選項。在文字中嵌入確定的位元組序列，大部分以 `ESC` 跳脫字元和 "[" 字元開始，終端會把這些位元組序列解釋為相應的指令，而不是普通的字元編碼。

ANSI 序列是在二十世紀七十年代引入的標準，用以取代特定於終端供應商的序列，並在二十世紀八十年代早期開始在電腦裝置市場上廣泛使用。與早期缺少游標移動功能的系統相比，新生的電子公告板系統使用 ANSI 序列改進其顯示。正是因為這個原因，ANSI 序列變成了所有製造商共同採用的標準。

在 21 世紀，儘管硬體文字終端已經越來越少了，但 ANSI 標準依然存在，因為大多數終端模擬器會對部分 ANSI 跳脫序列進行解釋。一個值得注意的例外是，在微軟 Windows 10 更新 TH2 之前，Windows 作業系統的 Win32 控制台是不支援 ANSI 跳脫序列的。

## 歷史

最初，幾乎每個影片終端製造商都各自添加了特定的跳脫序列用於執行一些特殊操作，比如把游標置於螢幕上的某個位置。舉例來說，VT52 終端允許通過傳送 `ESC` 字元、`y` 字元，後面跟上兩個等於 x,y 位置的數值加上 32 的字元（這是為了從 ASCII 空格字元開始，並避開控制字元），將游標置於螢幕上的 x,y 位置。

由於這些序列對於不同的終端並不一樣，因此人們不得不開發了一些複雜的庫（比如 termcap）和實用程式（比如 tput），以便程式可以使用同一套 API 應對各種終端。另外，在很多終端中需要藉助字元的二進位值傳送數字（如行和列）。對於某些程式語言，以及內部不使用 ASCII 的系統來說，把數字轉換為正確的字元常常是有困難的，甚至完全做不到。

ANSI 標準試圖解決這些問題。標準制訂了一種所有終端共享的指令集，並要求用 ASCII 的數字字元傳遞所有數值資訊。該系列的第一個標準是 1976 年通過的 ECMA-48。它是一系列字元編碼標準的延續，其中第一個是從 1965 年的 ECMA-6，一個 7 位標準，ISO 646 就源自此標準。「ANSI 跳脫序列」的名稱可以追溯到 1979 年 ANSI 採用 ANSI X3.64。此外，ANSI X3L2 委員會與 ECMA 委員會 TC 1 合作制訂了一個幾乎一模一樣的標準。以上兩個標準合併為 ISO 6429 的國際標準。1994 年，ANSI 取消了其標準，以支援國際標準。

第一個支援這個標準的流行影片終端是 1978 年推出的 Digital VT100。這個終端在市場上非常成功，引發了各種各樣的仿製品，其中最早和最流行的是 1979 年的 Zenith Z-19。其他品牌還有 Qume QVT-108，Televideo TVI-970，Wyse WY-99GT。另外，許多其他品牌的終端也不同程度地相容可選的「VT100」、「VT103」或「ANSI」模式。 隨著越來越多的軟體（尤其是 BBS 系統）普及，越來越多的軟體依賴跳脫序列起作用，導致幾乎所有新的終端和終端模擬器都支援了此標準。

1981 年，ANSI X3.64 被美國政府採用（FIPS 86）。後來，美國政府停止複製行業標準，所以 FIPS 86 又被撤回了。

ECMA-48 已經經歷了多次更新換代，目前是從 1991 年開始的第 5 版。它也被 ISO 和 IEC 用作標準 **ISO/IEC 6429**。

## 平台支援

### 類 Unix 系統和 AmigaOS

隨著諸多 BBS 和線上服務廣泛使用 ANSI，到 20 世紀 80 年代中期，ANSI 幾乎得到了全平台支援。儘管許多作業系統在標準文字輸出中越來越多地支援 ANSI，但大多數情況下 ANSI 支援是由終端模擬器完成的（例如 Unix/Linux 上的 xterm、GNOME 終端、Konsole，MacOS 上的 OS X Terminal 或 ZTerm，以及 IBM PC 上的許多通信程式）。

Unix 和 AmigaOS 都在作業系統中包含了對 ANSI 的一些支援，因此在這些平台上執行的程式廣泛使用 ANSI。 類 Unix 作業系統可以通過像 termcap 和 curses 函式庫之類的庫來生成 ANSI 代碼，許多軟體使用這些庫升級顯示方式。這些庫也應該支援非 ANSI 終端，但是現在很少有人測試，所以很可能已經不起作用了。許多遊戲和 shell 指令碼直接輸出 ANSI 序列（如彩色的提示資訊），因此無法在不支援 ANSI 的終端上執行。

AmigaOS 不僅支援輸出到螢幕上的文字使用 ANSI 序列，還支援印表機驅動程式使用 ANSI 序列（需要 AmigaOS 的專有擴充，這會將它們轉換為與特定印表機實際通信所需的代碼）。

儘管 ANSI 很普及，卻並沒有得到全平台支援。比如原始的「經典」Mac OS 就沒有內建對 ANSI 的支援，再比如 Atari ST 使用的是 VT52 改編的命令系統，需要用一些擴充程式來支援顏色顯示。

### Windows 和 DOS

MS-DOS 1.x 不支援 ANSI 或任何其他跳脫序列，只有少數控制字元（BEL、CR、LF、BS）可以由底層 BIOS 解釋，所以幾乎不可能做出任何全螢幕應用程式。所有顯示效果都必須通過 BIOS 呼叫，或者直接控制 IBM PC 硬體來完成，呼叫速度非常慢。

DOS 2.0 引入了添加裝置驅動程式來支援 ANSI 跳脫序列的功能（事實上的標準是 ANSI.SYS，但也使用了 ANSI.COM、NANSI.SYS 和 ANSIPLUS.EXE 等其他程式。因為繞過了 BIOS，所以這些程式的速度比以前快了不少）。但由於實際執行速度仍然比較慢，以及預設並沒有安裝，所以還是很少得到利用。應用程式往往還是繼續用直接控制硬體的方式來顯示所需的文字。ANSI.SYS 和類似的驅動程式繼續在 Windows 9x 上工作，直到 Windows Me，在 NT 衍生系統中用於在 NTVDM 下執行的 16 位元傳統程式。

Win32 控制台完全不支援 ANSI 跳脫序列。不過有一些控制台的替代品或者附加軟體具有解釋程式輸出的 ANSI 跳脫序列的功能，例如 JP Software 的 TCC（以前的 4NT）、Michael J. Mefford 的 ANSI.COM、Jason Hood 的 ANSICON 和 Maximus5 的 ConEmu。有一個 Python 軟體套件在內部解釋了列印文字中的 ANSI 跳脫序列，將它們轉換為系統呼叫來操縱顏色和游標位置，以便更容易地將使用 ANSI 的 Python 代碼移植到 Windows。

2016 年，在 Windows 10 發布「Threshold 2」時，微軟開始在控制台應用程式中支援 ANSI 跳脫序列，使得從 Unix 移植軟體或者遠端存取 Unix 變得更容易。這是與 Windows Subsystem for Linux 一起完成的，允許基於終端的類 Unix 軟體在 Win32 控制台中使用跳脫序列。2019 年，微軟推出了 Windows Terminal，並且正在用其替換掉 Win32 控制台

## 跳脫序列

序列具有不同的長度。所有序列都以 ASCII 字元 `**ESC**`（27 / 十六進位 0x1B）開頭，第二個位元組則是 0x40–0x5F（ASCII `@A–Z[\]^_`）範圍內的字元。

標準規定，在 8 位元環境中，這兩個位元組的序列可以合併為 0x80-0x9F 範圍內的單個位元組（詳情請參閱 C1 控制字元集）。但是，在現代裝置上，這些代碼通常用於其他目的，例如 UTF-8 的一部分或 CP-1252 字元，因此並不使用這種合併的方式。

除 ESC 之外的其他 C0 代碼（通常是 BEL，BS，CR，LF，FF，TAB，VT，SO 和 SI）在輸出時也可能會產生與某些控制序列相似或相同的效果。

按下鍵盤上的特殊鍵，以及輸出 xterm CSI、DCS 或 OSC 序列，常常用於產生從終端傳送到電腦的 CSI，DCS 或 OSC 序列，就像使用者使用鍵盤輸入的一樣。

## CSI 序列

CSI 序列由 `ESC [`、若干個（包括 0 個）「參數位元組」、若干個「中間位元組」，以及一個「最終位元組」組成。各部分的字元範圍如下：

所有常見的序列都只是把參數用作一系列分號分隔的數字，如 `1;2;3`。缺少的數字視為 0（如 `1;;3` 相當於中間的數字是 0，`ESC[m` 這樣沒有參數的情況相當於參數為 0）。某些序列（如 CUU）把 0 視為 1，以使缺少參數的情況下有意義。

一部分字元定義是「私有」的，以便終端製造商可以插入他們自己的序列而不與標準相衝突。包括參數位元組 `<=>?` 的使用，或者最終位元組 0x70–0x7F（`p–z{|}~`）例如 VT320 序列 `CSI?25h` 和 `CSI?25l` 的作用是打開和關閉游標的顯示。

當 CSI 序列含有超出 0x20–0x7E 範圍的字元時，其行為是未定義的。這些非法字元包括 C0 控制字元（範圍 0–0x1F）、DEL（0x7F），以及高位位元組。

## 選擇圖形再現（SGR）參數

## 顏色

### 3/4 位元

初始的規格只有 8 種顏色，只給了它們的名字。SGR 參數 30-37 選擇前景色，40-47 選擇背景色。相當多的終端將「粗體」（SGR 代碼 1）實現為更明亮的顏色而不是不同的字型，從而提供了 8 種額外的前景色，但通常情況下並不能用於背景色，雖然有時候反顯（SGR 代碼 7）可以允許這樣。例如：在白色背景上顯示黑色文字使用 `ESC[30;47m`，顯示紅色文字用 `ESC[31m`，顯示明亮的紅色文字用 `ESC[1;31m`。重設為預設顏色用 `ESC[39;49m`（某些終端不支援），重設所有屬性用 `ESC[0m`。後來的終端新增了功能，可以直接用 90-97 和 100-107 指定「明亮」的顏色。

當硬體開始使用 8 位元 DAC 時，多個軟體為這些顏色名稱分配了 24 位元的代碼。下面的圖表顯示了傳送到 DAC 的一些常用硬體和軟體的值。

### 8 位元

隨著 256 色尋找表在顯示卡上越來越常見，相應的跳脫序列也增加了，以從預定義的 256 種顏色中選擇：

```
   ESC[ … 38;5;<n> … m选择前景色（n是下表中的一种）
   ESC[ … 48;5;<n> … m选择背景色
     0-  7：标准颜色（同ESC [ 30–37 m）
     8- 15：高强度颜色（同ESC [ 90–97 m）
    16-231：6 × 6 × 6 立方（216色）: 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
   232-255：从黑到白的24阶灰度色
```

ITU 的 T.416 資訊科技 - 開放文件體系結構（ODA）和交換格式：字元內容體系結構使用「:」作為分隔符：

```
   ESC[ … 38:5:<n> … m选择前景色（n是下表中的一种）
   ESC[ … 48:5:<n> … m选择背景色
```

### 24 位元

隨著 16 位元到 24 位元顏色的「真彩色」顯示卡的普及，Xterm、KDE 的 Konsole，以及所有基於 libvte 的終端（包括 GNOME 終端）支援了 ISO-8613-3 的 24 位元前景色和背景色設定。

```
   ESC[ … 38;2;<r>;<g>;<b> … m选择RGB前景色
   ESC[ … 48;2;<r>;<g>;<b> … m选择RGB背景色
```

作為 ISO / IEC 國際標準 8613-6 採用的 ITU 的 T.416 資訊科技 - 開放文件體系結構（ODA）和交換格式：字元內容體系結構給出了一個似乎不太受支援的替代版本：

```
   ESC[ … 38:2:<Color-Space-ID>:<r>:<g>:<b>:<unused>:<CS tolerance>:<Color-Space: 0="CIELUV"; 1="CIELAB">m选择RGB前景色
   ESC[ … 48:2:<Color-Space-ID>:<r>:<g>:<b>:<unused>:<CS tolerance>:<Color-Space: 0="CIELUV"; 1="CIELAB">m选择RGB背景色
```

請注意，這裡使用了保留的「:」字元來分隔子選項，這可能是在實際實現中造成混淆的始作俑者。它還使用「3」作為第二個參數來指定使用青 - 品紅 - 黃方案的方案，「4」用於青 - 品紅 - 黃 - 黑的方案，後者使用上面標記為「unused」（「未使用」）的位置作為黑色組件。

還要注意，許多辨識「:」作為分隔符的實現錯誤地忽視了色彩空間識別碼參數，並因此改變了其餘部分的位置。

## 範例

`CSI 2 J` — 清除螢幕、（在某些裝置上）把游標置於 1,1 位置（左上角）。

`CSI 32 m` — 使文字呈綠色。在 MS-DOS 上，一般綠色是暗淡的綠色，可以用 `CSI 1 m` 啟用粗體使其變成明亮的綠色，或者將兩者合併為 `CSI 32 ; 1 m`。MS-DOS ANSI.SYS 用粗體狀態使字元變亮，閃爍狀態（通過 `INT 10, AX 1003h, BL 00h`) 使背景色變成明亮模式。MS-DOS ANSI.SYS 並不直接支援 SGR 代碼 90–97 和 100–107。

`CSI 0 ; 6 8 ; "DIR" ; 13 p` — 重新分配 F10 鍵的功能為傳送字串「DIR」和回車字元到鍵盤快取中，在 DOS 命令列里會顯示當前目錄的內容（僅 MS-DOS ANSI.SYS）。這種序列有時用於「ANSI 炸彈」。這是一個私用編碼（如字母 p 所示），用非標準的擴充使其包含一個字串參數。如果按標準，會認為字母 D 是序列的末尾。

`CSI s` — 儲存游標的位置。用序列 `CSI u` 會把游標重設回這個位置。假設當前的游標位置是 7（y）、10（x）。序列 `CSI s` 會儲存這兩個數值。現在可以把游標移動到其他位置，比如用序列 `CSI 20 ; 3 H` 或 `CSI 20 ; 3 f` 把游標移動到 20（y）、3（x）。現在如果用序列 `CSI u`，游標會回到 7（y）、10（x）。某些終端需要使用 DEC 序列 `ESC 7`/`ESC 8`，這得到了更廣泛的支援。

### 使用 Shell 指令碼的範例

ANSI 轉移代碼常常用於 UNIX 和類 UNIX 終端，以提供語法突顯功能。例如，在相容的終端上，以下 ls 命令按類型對檔案和目錄的名稱進行顏色編碼。

```
ls --color
```

使用者可以在指令碼中使用跳脫碼，將其作為標準輸出或標準錯誤輸出的一部分。例如，下面的 GNU sed 命令通過反顯「WARN」開頭的單詞的行，以及使用暗紅色背景色和亮黃色前景色顯示以「ERR」開頭的單詞（字母大小寫被忽略）的行來修飾 make 命令的輸出。突出顯示了設定 ANSI 代碼的部分。

```
make 2>&1 | sed -e 's/.\*\bWARN.\*/\x1b[7m&\x1b[0m/i' -e 's/.\*\bERR.\*/\x1b[93;41 m&\x1b[0m/i'
```

以下 Bash 函式會使終端閃爍（通過交替傳送反相和正常顯示模式代碼），直到使用者按下任意鍵。這個函式可以用於當一個冗長的命令終止時提醒使用者，用法如 `make; flasher`。

```
flasher () { while true; do printf \\e[?5h; sleep 0.1; printf \\e[?5l; read -s -n1 -t1 && break; done; }
```

下面這個命令可以重設控制台，類似現代 Linux 系統的 `reset` 命令。然而，即使在較早的 Linux 系統和其他（非 Linux）UNIX 變體上，也應該能起作用。

```
printf \\033c
```

## 參見

* 控制字元

## 註腳

## 參考資料

## 外部連結

* Standard ECMA-48, Control Functions For Coded Character Sets（頁面存檔備份，存於網際網路檔案館）. (_5th edition, June 1991_), European Computer Manufacturers Association, Geneva 1991 (also published by ISO and IEC as standard ISO/IEC 6429)
* vt100.net DEC DocumentsArchive.is 的存檔，存檔日期 2013-02-22
* ANSI.SYS -- ansi terminal emulation escape sequences. [2018-01-12]. （原始內容存檔於 2006-02-06）.
* Xterm / Escape Sequences（頁面存檔備份，存於網際網路檔案館）
* AIXterm / Escape Sequences
* A collection of escape sequences for terminals that are vaguely compliant with ECMA-48 and friends.（頁面存檔備份，存於網際網路檔案館）
* ANSI Escape Sequences（頁面存檔備份，存於網際網路檔案館）
* ITU-T Rec. T.416 (03/93) Information technology – Open Document Architecture (ODA) and interchange format: Character content architectures（頁面存檔備份，存於網際網路檔案館）