エスケープシーケンスを体感する
エスケープシーケンス(escape sequence)とは、直訳すれば「エスケープに続く数列(文字コード列)」といった意味合いになると思う。広い意味では、特別な文字表記(エスケープ記号)で始まる一連の文字列を連想してしまう。例えば、\nが改行を表現するのも広い意味ではエスケープシーケンスに含まれる。しかし、ここではもっと狭い意味のエスケープシーケンスを扱う。ASCII制御コードを拡張するためのエスケープシーケンスだ。
前回調べたように、ASCII制御コード(\x00〜\x20、\x7f)はテレタイプ端末で通信することを想定した古い規格になっている。その古いASCII制御コードだけでは、時代と共に進化してきたビデオ端末や端末エミュレータを満足に制御できるはずがない。新たな制御コードを追加する必要があったのだ!
ところが、新たな制御コードを追加するにも、たったの128文字しかないASCIIコード表には空きがない。ではどうしたかというと、ASCII制御コードのESC(\x1B)を利用して拡張したのだ。ESC(\x1B)で始まる複数の文字コードの連続によって、様々な制御機能を追加している。拡張されたASCII制御コードを体感してみる。
コード領域の基礎知識
- ASCIIコードは7ビットの文字コードである。(\x00〜\x7F)
- 表現できる文字数を増やすため8ビットの文字コードが考案された時、それぞれの領域を以下のように区別するようになった。
- C0のC = Control(制御)のC
- GLのG = Graphic(図形)のG
- 一方、従来の7ビットコード体系から拡張されたC1領域を使いたい需要もあった。
- そこで、制御コード ESCを利用して、以下のように指定する仕組みとなった。
-
- ASCII制御コードをControlキーと@ A〜Z [ \ ] ^ _で入力する仕組みと同じように、
- 例:^@、^G、^[
- ASCII制御コードをControlキーと@ A〜Z [ \ ] ^ _で入力する仕組みと同じように、
-
- C1領域の制御コードは制御コード ESCと@ A〜Z [ \ ] ^ _で指定できる。
- 例:\e@、\eG、\e[
- ESC = \e = \x1B = \033
- C1領域の制御コードは制御コード ESCと@ A〜Z [ \ ] ^ _で指定できる。
- つまり、8ビットコード体系の\x80〜\x9Fは、7ビットコード体系では\x1B40〜\x1B5Fまでの2バイトで表現される!
ESC(\x1B)の制御コードで始まる数列なのでエスケープシーケンスと呼ばれるのだ。
C1以外の制御コード
- さらに、ESC(\x1B)を利用することで、それに続く文字コードは何でもエスケープシーケンスと認識できる。
- もはや、32文字のC1領域だけの表現にこだわる必要はなくなった。
- 印字可能な94文字すべてを使って制御コードを拡張できるのだ!
ISO 2022の文字コード制御
略語 | 意味 | ESC シーケンス |
||
---|---|---|---|---|
SI or LS0 | GL←G0呼び出し(ロッキングシフト) | ^N | ||
SO or LS1 | GL←G1呼び出し(ロッキングシフト) | ^O | ||
LS2 | GL←G2呼び出し(ロッキングシフト) | \en | ||
LS3 | GL←G3呼び出し(ロッキングシフト) | \eo | ||
LS1R | GR←G1呼び出し(ロッキングシフト) | \e~ | ||
LS2R | GR←G2呼び出し(ロッキングシフト) | \e} | ||
LS3R | GR←G3呼び出し(ロッキングシフト) | \e | ||
SS2 | G2の1文字限りの呼び出し(シングルシフト) | \eN | ||
SS3 | G3の1文字限りの呼び出し(シングルシフト) | \eO | ||
GZD4 | G0←94文字集合の指示(1バイトコード) | \e( | ||
G1D4 | G1←94文字集合の指示(1バイトコード) | \e) | ||
G2D4 | G2←94文字集合の指示(1バイトコード) | \e* | ||
G3D4 | G3←94文字集合の指示(1バイトコード) | \e+ | ||
G1D6 | G1←96文字集合の指示(1バイトコード) | \e- | ||
G2D6 | G2←96文字集合の指示(1バイトコード) | \e. | ||
G3D6 | G3←96文字集合の指示(1バイトコード) | \e/ | ||
GZDM4 | G0←94のn乗文字集合の指示(複数バイトコード) | \e$( | ||
G1DM4 | G1←94のn乗文字集合の指示(複数バイトコード) | \e$) | ||
G2DM4 | G2←94のn乗文字集合の指示(複数バイトコード) | \e$* | ||
G3DM4 | G3←94のn乗文字集合の指示(複数バイトコード) | \e$+ | ||
G1DM6 | G1←96のn乗文字集合の指示(複数バイトコード) | \e$- | ||
G2DM6 | G2←96のn乗文字集合の指示(複数バイトコード) | \e$. | ||
G3DM6 | G3←96のn乗文字集合の指示(複数バイトコード) | \e$/ | ||
S7C1T | 7ビットモード。応答に7ビットシーケンスを使う。 | \eSP F | ||
S8C1T | 8ビットモード。応答に8ビットシーケンスを使う。 | \eSP G |
カーソル・表示・キー入力制御
略語 | 意味 | ESC シーケンス |
||
---|---|---|---|---|
DECSC | 保存する(カーソル位置とテキスト属性) | \e7 | ||
DECRC | 復元する(カーソル位置とテキスト属性) | \e8 | ||
DECKPAM | アプリケーションキーパッドモードに設定する | \e= | ||
DECKPNM | 数値キーパッドモードに設定する | \e> | ||
RIS | 端末リセット(画面クリア・カーソル左上・タブリセット) | \ec | ||
DECDHL | カーソル行の表示倍率を設定(縦倍・横倍して上側を表示) | \e#3 | ||
DECDHL | カーソル行の表示倍率を設定(縦倍・横倍して下側を表示) | \e#4 | ||
DECSWL | カーソル行の表示倍率を設定(標準表示) | \e#5 | ||
DECDWL | カーソル行の表示倍率を設定(横倍表示) | \e#6 | ||
DECALN | 画面を'E'で埋める | \e#8 |
C1領域の制御コード
- C1領域の制御コードには、以下のような意味がある。
略語 | 由来語句 | 意味 | 8ビットの 16進数コード |
ESC シーケンス |
---|---|---|---|---|
PAD | Padding Character | \x80 | \e@ | |
HOP | High Octet Preset | \x81 | \eA | |
BPH | Break Permitted Here | 分割許可 | \x82 | \eB |
NBH | No Break Here | 分割禁止 | \x83 | \eC |
IND | Index | カーソルを次の行へ移動 | \x84 | \eD |
NEL | NExt Line | カーソルを次の行の先頭に移動 | \x85 | \eE |
SSA | Start of Selected Area | \x86 | \eF | |
ESA | End of Selected Area | \x87 | \eG | |
HTS | Horizontal Tabulation Set | 現在のカーソル位置に水平タブストップを設定する | \x88 | \eH |
HTJ | Horizontal Tabulation with Justification | \x89 | \eI | |
VTS | Vertical Tabulation Set | 現在のカーソル位置に垂直タブストップを設定する | \x8A | \eJ |
PLD | Partial Line Down | 下付き文字にする | \x8B | \eK |
PLU | Partial Line Up | 上付き文字にする | \x8C | \eL |
RI | Reverse Index | カーソルを前の行へ移動 | \x8D | \eM |
SS2 | Single Shift 2 | GL または GR へ G2 を次の一文字に限り呼び出す | \x8E | \eN |
SS3 | Single Shift 3 | GL または GR へ G3 を次の一文字に限り呼び出す | \x8F | \eO |
DCS | Device Control String | DCSシーケンスを開始、STで終了 | \x90 | \eP |
PU1 | Private Use 1 | \x91 | \eQ | |
PU2 | Private Use 2 | \x92 | \eR | |
STS | Set Transmit State | \x93 | \eS | |
CCH | Cancel Character | \x94 | \eT | |
MW | Message Waiting | \x95 | \eU | |
SPA | Start of Protected Area | \x96 | \eV | |
EPA | End of Protected Area | \x97 | \eW | |
SOS | Start Of String | SOSシーケンスを開始、STで終了 | \x98 | \eX |
SGCI | Single Graphic Character Introducer | \x99 | \eY | |
SCI | Single Character Introducer | \x9A | \eZ | |
CSI | Control Sequence Introducer | CSIシーケンスを開始 | \x9B | \e[ |
ST | String Terminator | DCS, SOS, OSC, PM, APC シーケンスの終端 | \x9C | \e\ |
OSC | Operating System Command | OSCシーケンスを開始、STで終了 | \x9D | \e] |
PM | Privacy Message | PMシーケンスを開始、STで終了 | \x9E | \e^ |
APC | Application Program Command | APCシーケンスを開始、STで終了 | \x9F | \e_ |
シーケンスの拡張
- C1領域の制御コードの中でもシーケンス関連のコードは、それに続く複数のコードを引数にして、さらに機能を拡張する。
DCS = \x1B50 SOS = \x1B58 CSI = \x1B5B OSC = \x1B5D PM = \x1B5E APC = \x1B5F
- ESC(\x1B)が制御コードを拡張し、続く2文字目のコードでエスケープシーケンスをさらに拡張している。
-
-
- どのようなシーケンスが存在するかは、以下のページがかなり詳しい。
- 対応制御シーケンス - Tera Term ヘルプ
- エスケープシーケンス一覧 - rlogin/telnet/ssh(クライアント)ターミナルソフト
-
CSI(Control Sequence Introducer)
- 中でも画面出力をコントロールするCSIシーケンス(\e[ = \x1B5B)は重要である。
- CSIシーケンスを使えば、カーソル位置や文字色・背景色・明るさ・下線・点滅・反転などのテキスト属性を自由に設定できる。
- CSIシーケンスは、\e[に続けて引数と終了文字を指定する。
- 終了文字が動作を決定する。
- 引数でその動作の詳細を指定する。
例:
CSI | 引数 | 終了文字 | 意味 |
---|---|---|---|
\e[ | 2 | A | 2行上にカーソルを移動する |
\e[ | A | 1行上にカーソルを移動する(デフォルト値 = 1が利用された) | |
\e[ | 10;8 | H | 10行8列へカーソルを移動する( ; で区切って二つの引数を指定した) |
-
- 引数は10進数で指定する。
- 引数は ; で区切って複数指定できる。(最大16個)
- 引数を省略するとデフォルト値が利用される。
- n行m列などの位置情報を意味する引数のデフォルト値 = 1
- 機能を選択する引数のデフォルト値 = 0
- すべては網羅できないが、よく使いそうなCSIシーケンスを調べてみた。
略語 | 意味 | ESC シーケンス |
||
---|---|---|---|---|
CUU | カーソル移動(n行上へ) | \e[nA | ||
CUD | カーソル移動(n行下へ) | \e[nB | ||
CUF | カーソル移動(n文字右へ) | \e[nC | ||
CUB | カーソル移動(n文字左へ) | \e[nD | ||
CNL | カーソル移動(n行下の行頭へ) | \e[nE | ||
CPL | カーソル移動(n行上の行頭へ) | \e[nF | ||
CHA | カーソル移動(n列へ) | \e[nG | ||
CUP | カーソル移動(n行m列へ) | \e[n;mH | ||
CHT | カーソル移動(カーソル行のn個次のタブ位置へ) | \e[nI | ||
CBT | カーソル移動(カーソル行のn個前のタブ位置へ) | \e[nZ | ||
ED | テキストクリア(カーソル位置から画面末尾まで) | \e[J | ||
ED | テキストクリア(カーソル位置から画面先頭まで) | \e[1J | ||
ED | テキストクリア(画面全体) | \e[2J | ||
EL | テキストクリア(カーソル位置から行末まで) | \e[K | ||
EL | テキストクリア(カーソル位置から行頭まで) | \e[1K | ||
EL | テキストクリア(1行全体) | \e[2K | ||
ECH | テキストクリア(カーソル位置右側をn文字、SPに置き換え) | \e[nX | ||
DCH | テキスト削除(カーソル位置右側をn文字、左にスライド) | \e[nP | ||
IL | n行挿入 | \e[nL | ||
DL | n行削除 | \e[nM | ||
TBC | タブ削除(カーソル位置のタブ) | \e[g | ||
TBC | タブ削除(すべてのタブ) | \e[3g | ||
DECSTBM | スクロール範囲を設定(画面全体) | \e[r | ||
DECSTBM | スクロール範囲を設定(n行からm行まで) | \e[n;mr | ||
SU | スクロールする(n行上へ) | \e[nS | ||
SD | スクロールする(n行下へ) | \e[nT | ||
REP | 直前の文字をn回繰り返す | \e[nb | ||
DSR | 端末の状態を返す(例:^[[0n = 正常) | \e[5n | ||
DSR | カーソル位置を返す(例:^[[54;1R = 54行1列) | \e[6n | ||
DECTCEM | 拡張オプションの設定(カーソルを隠す) | \e[?25l | ||
DECTCEM | 拡張オプションの設定(カーソルを表示) | \e[?25h | ||
SGR | テキスト属性の設定 | \e[n1;n2;...m |
SGR(Select Graphics Rendition)
- さらに、テキスト属性の設定は、CSIシーケンスのSGRという設定によって行う仕組みである。
- SGR(\e[m = \x1B5B6D)に複数の引数を ; で区切って与える(\e[n1;n2;...m)ことで、文字色・背景色・明るさ・下線・点滅・反転などを自由に指定できる。
例:
CSI | 引数 | 終了文字SGR | 意味 |
---|---|---|---|
\e[ | 1;31;46 | m | 太字・文字色=赤・背景色=水色に設定する(\e[mなどでリセットするまで効果は続く) |
\e[ | 4;38;5;150;48;5;200 | m | 下線・文字色=150・背景色=200に設定する |
- 太字・低輝度・下線・点滅・反転・非表示の引数
番号 | 意味 | 番号 | 意味 |
---|---|---|---|
0 | すべての属性を解除 | ||
1 | 太字 | 21 | |
2 | 低輝度 | 22 | 太字解除・低輝度解除 |
3 | 23 | ||
4 | 下線 | 24 | 下線解除 |
5 | 点滅 | 25 | 点滅解除 |
6 | 26 | ||
7 | 反転 | 27 | 反転解除 |
8 | 非表示 | 28 | 非表示解除 |
- 色の引数
番号 | 意味 | 番号 | 意味 | 番号 | 意味 | 番号 | 意味 |
---|---|---|---|---|---|---|---|
30 | 文字色を黒 | 40 | 背景色を黒 | 90 | 文字色を明るい黒(灰色) | 100 | 背景色を明るい黒(灰色) |
31 | 文字色を赤 | 41 | 背景色を赤 | 91 | 文字色を明るい赤 | 101 | 背景色を明るい赤 |
32 | 文字色を緑 | 42 | 背景色を緑 | 92 | 文字色を明るい緑 | 102 | 背景色を明るい緑 |
33 | 文字色を黄色 | 43 | 背景色を黄色 | 93 | 文字色を明るい黄色 | 103 | 背景色を明るい黄色 |
34 | 文字色を青 | 44 | 背景色を青 | 94 | 文字色を明るい青 | 104 | 背景色を明るい青 |
35 | 文字色を赤紫 | 45 | 背景色を赤紫 | 95 | 文字色を明るい赤紫 | 105 | 背景色を明るい赤紫 |
36 | 文字色を水色 | 46 | 背景色を水色 | 96 | 文字色を明るい水色 | 106 | 背景色を明るい水色 |
37 | 文字色を白 | 47 | 背景色を白 | 97 | 文字色を明るい白 | 107 | 背景色を明るい白 |
38;5;n | 拡張文字色をn(0〜255) | 48;5;n | 拡張背景色をn(0〜255) | ||||
39 | 文字色を標準 | 49 | 背景色を標準 |
- 一桁目の数値は、色コードを表現している。
- RGBを3ビットで表現すると、0〜7の数値に変換できるのだ。
B | G | R | 番号 | 色 | Color |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 黒 | Black |
0 | 0 | 1 | 1 | 赤 | Red |
0 | 1 | 0 | 2 | 緑 | Green |
0 | 1 | 1 | 3 | 黄色 | Yellow |
1 | 0 | 0 | 4 | 青 | Blue |
1 | 0 | 1 | 5 | 赤紫 | Magenta |
1 | 1 | 0 | 6 | 水色 | Cyan |
1 | 1 | 1 | 7 | 白 | White |
体感してみる
以上のエスケープシーケンスを理解して、いよいよ体感してみるのだ!
環境と方法
$ bash --version GNU bash, バージョン 4.3.33(1)-release (x86_64-apple-darwin14.1.0) Copyright (C) 2013 Free Software Foundation, Inc. ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 http://gnu.org/licenses/gpl.html
- ターミナル.app上で、実際にエスケープシーケンスを含む文字列を表示してみるのが一番分かり易いはず。
- その際、printfコマンドを使った方が、無用な悩みを排除できる。
256色を体感する
上記SRGの指定に従えば、ターミナル.appは256色を発色する性能を持っている!
- すべての色を表示してみた。
for c in {0..255}; do printf "\033[48;5;${c}m%8d\033[m" $c; done; echo
- 実は、0〜255の色コードは3つに分類できる。
0〜 15の基本色 | 「黒・赤・緑・黄色・青・赤紫・水色・白」の暗いセットと明るいセットの16色 |
---|---|
16〜231の中間色 | RGBの中間色6段階の輝度を混ぜ合わせた216色 |
232〜255のモノクロ | 黒から白まで輝度を16段階に区切ったモノクロ16色 |
for c in {0..7}; do printf "\033[48;5;${c}m%6d\033[m" $c; done; echo; for c in {8..15}; do printf "\033[48;5;${c}m%6d\033[m" $c; done; echo for c in {16..231}; do printf "\033[48;5;${c}m%6d\033[m" $c; done; echo for c in {232..255}; do printf "\033[48;5;${c}m%6d\033[m" $c; done; echo
- さらに中間色について、数値優先の並べ方をやめて、色の変化優先の並べ方にしてみる。
ruby -e ' (0..4).each{|r| g=5;(0..5).each{|b|printf("\e[48;5;#{ r*36 + g*6 + b + 16}m%11d \e[m", r*36 + g*6 + b + 16)}; puts}; (0..5).each{|g| r=5;(0..5).each{|b|printf("\e[48;5;#{ r*36 + 30 - g*6 + b + 16}m%11d \e[m", r*36 + 30 - g*6 + b + 16)}; b=5;(1..5).each{|r|printf("\e[48;5;#{180 - r*36 + 30 - g*6 + b + 16}m%11d \e[m", 180 - r*36 + 30 - g*6 + b + 16)}; puts}'
これで速やかに、好みの色コードを選択できるようになった!
太字・低輝度・下線・点滅・反転・非表示を体感する
- リセット(\e[m)しなければ、直前のテキスト属性も引き継いで反映される。
for c in 0 30 31 32 33 34 35 36 37 do printf "\e[${c}mNormal \e[${c};1mBold \e[${c};2mDim \e[${c};4mUnderLine \e[${c};5mBlink \e[${c};7mReverse\e[m \e[${c};8mHide \e[m<--Hide\n" done
- リセット(\e[m)することで、設定したテキスト属性のみ反映される。
for c in 0 30 31 32 33 34 35 36 37 do printf "\e[${c}mNormal \e[${c};1mBold\e[m \e[${c};2mDim\e[m \e[${c};4mUnderLine\e[m \e[${c};5mBlink\e[m \e[${c};7mReverse\e[m \e[${c};8mHide \e[m<--Hide\n" done
クルクル回す
- CSIシーケンスのカーソル移動を利用して、| を回転させてみた。
for i in `seq 1 10` do printf "\e[D|" ; sleep 0.2; printf "\e[D/" ; sleep 0.2; printf "\e[D-" ; sleep 0.2; printf "\e[D\\"; sleep 0.2; done
- もっとも、| を回転させるだけなら、ASCII制御コードのバックスペースでも実現できるのだけど...。
for i in `seq 1 10` do printf "\b|" ; sleep 0.2; printf "\b/" ; sleep 0.2; printf "\b-" ; sleep 0.2; printf "\b\\"; sleep 0.2; done
プログレスバーを作る
printf "\e7|-\e[-98b|"; for i in {0..100}; do printf "\e8|\e[${i}b"; sleep 0.05; done; echo
||||||||||||||||||||||||||||||||||||||||||----------------------------------------------------------|
端末リセット
printf "\ec"
-
- いちばん下までスクロールして画面に表示されているテキストのみクリアされた。
- 画面に表示されていないスクロール領域のテキストは残った。
- カーソルは画面左上に移動された。
- タブは8文字ごとにリセットされた。
縦倍・横倍表示
printf "\e#3ABCDEFG\n" printf "\e#4ABCDEFG\n" printf "\e#3ABCDEFG\n"; printf "\e#4ABCDEFG\n" printf "\e#5ABCDEFG\n" printf "\e#6ABCDEFG\n"
-
- 縦横倍にする時は、上側と下側に分けて2行で表示する仕組みのようだ。
参考ページ
- 以下のページがたいへん参考になりました。感謝です!
ISO 2022について
- ANSI escape code - Wikipedia, the free encyclopedia
- Bash Prompt HOWTO: ANSI エスケープシーケンス: 色とカーソル操作
- ASCII Table - ANSI Escape sequences
VT-100
xterm
エスケープシーケンスの詳細
Linux console_codesについて