Hatena::ブログ(Diary)

ザリガニが見ていた...。 このページをアンテナに追加 RSSフィード

2015-02-24

エスケープシーケンスを体感する

エスケープシーケンス(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

f:id:zariganitosh:20150220075855p:image:w450


  • 一方、従来の7ビットコード体系から拡張されたC1領域を使いたい需要もあった。
  • そこで、制御コード ESCを利用して、以下のように指定する仕組みとなった。
    • ASCII制御コードをControlキー@ A〜Z [ \ ] ^ _で入力する仕組みと同じように、
      • 例:^@、^G、^[
    • C1領域の制御コードは制御コード ESC@ A〜Z [ \ ] ^ _で指定できる。
      • 例:\e@、\eG、\e[
      • ESC = \e = \x1B = \033
  • つまり、8ビットコード体系の\x80\x9Fは、7ビットコード体系では\x1B40\x1B5Fまでの2バイトで表現される!

f:id:zariganitosh:20150219173713p:image:w450

ESC(\x1B)の制御コードで始まる数列なのでエスケープシーケンスと呼ばれるのだ。

C1以外の制御コード

  • さらに、ESC(\x1B)を利用することで、それに続く文字コードは何でもエスケープシーケンスと認識できる。
  • もはや、32文字のC1領域だけの表現にこだわる必要はなくなった。
  • 印字可能な94文字すべてを使って制御コードを拡張できるのだ!
ISO 2022の文字コード制御
  • 例えば、ISO 2022はC1領域以外のエスケープシーケンスを利用して必要な文字集合を切り替えている。
      • 但し、ISO 2022のエスケープシーケンスは独自に文字コードを制御したい需要でもない限り、覚える必要はなさそう。
      • ほとんどの場合、OSやエディタ・メールなどがサポートするテキスト入力環境が、良きに計らいうまく処理してくれる。
      • iconvなどの文字コード変換ライブラリを使えば、このようなエスケープシーケンスを知らなくても変換できてしまう。
      • 文字化けしてしまったメールを自動で直すコンバーターとか作りたくなったら、その時に覚えるだけで十分。

略語意味ESC
シーケンス
SI or LS0GL←G0呼び出し(ロッキングシフト)^N
SO or LS1GL←G1呼び出し(ロッキングシフト)^O
LS2GL←G2呼び出し(ロッキングシフト)\en
LS3GL←G3呼び出し(ロッキングシフト)\eo
LS1RGR←G1呼び出し(ロッキングシフト)\e~
LS2RGR←G2呼び出し(ロッキングシフト)\e}
LS3RGR←G3呼び出し(ロッキングシフト)\e
SS2G2の1文字限りの呼び出し(シングルシフト)\eN
SS3G3の1文字限りの呼び出し(シングルシフト)\eO
GZD4G0←94文字集合の指示(1バイトコード\e(
G1D4G1←94文字集合の指示(1バイトコード)\e)
G2D4G2←94文字集合の指示(1バイトコード)\e*
G3D4G3←94文字集合の指示(1バイトコード)\e+
G1D6G1←96文字集合の指示(1バイトコード)\e-
G2D6G2←96文字集合の指示(1バイトコード)\e.
G3D6G3←96文字集合の指示(1バイトコード)\e/
GZDM4G0←94のn乗文字集合の指示(複数バイトコード)\e$(
G1DM4G1←94のn乗文字集合の指示(複数バイトコード)\e$)
G2DM4G2←94のn乗文字集合の指示(複数バイトコード)\e$*
G3DM4G3←94のn乗文字集合の指示(複数バイトコード)\e$+
G1DM6G1←96のn乗文字集合の指示(複数バイトコード)\e$-
G2DM6G2←96のn乗文字集合の指示(複数バイトコード)\e$.
G3DM6G3←96のn乗文字集合の指示(複数バイトコード)\e$/
S7C1T7ビットモード。応答に7ビットシーケンスを使う。\eSP F
S8C1T8ビットモード。応答に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
シーケンス
PADPadding Character \x80\e@
HOPHigh Octet Preset \x81\eA
BPHBreak Permitted Here分割許可\x82\eB
NBHNo Break Here分割禁止\x83\eC
INDIndexカーソルを次の行へ移動\x84\eD
NELNExt Lineカーソルを次の行の先頭に移動\x85\eE
SSAStart of Selected Area \x86\eF
ESAEnd of Selected Area \x87\eG
HTSHorizontal Tabulation Set現在のカーソル位置に水平タブストップを設定する\x88\eH
HTJHorizontal Tabulation with Justification \x89\eI
VTSVertical Tabulation Set現在のカーソル位置に垂直タブストップを設定する\x8A\eJ
PLDPartial Line Down下付き文字にする\x8B\eK
PLUPartial Line Up上付き文字にする\x8C\eL
RIReverse Indexカーソルを前の行へ移動\x8D\eM
SS2Single Shift 2GL または GR へ G2 を次の一文字に限り呼び出す\x8E\eN
SS3Single Shift 3GL または GR へ G3 を次の一文字に限り呼び出す\x8F\eO
DCSDevice Control StringDCSシーケンスを開始、STで終了\x90\eP
PU1Private Use 1 \x91\eQ
PU2Private Use 2 \x92\eR
STSSet Transmit State \x93\eS
CCHCancel Character \x94\eT
MWMessage Waiting \x95\eU
SPAStart of Protected Area \x96\eV
EPAEnd of Protected Area \x97\eW
SOSStart Of StringSOSシーケンスを開始、STで終了\x98\eX
SGCISingle Graphic Character Introducer \x99\eY
SCISingle Character Introducer \x9A\eZ
CSIControl Sequence IntroducerCSIシーケンスを開始\x9B\e[
STString TerminatorDCS, SOS, OSC, PM, APC シーケンスの終端\x9C\e\
OSCOperating System CommandOSCシーケンスを開始、STで終了\x9D\e]
PMPrivacy MessagePMシーケンスを開始、STで終了\x9E\e^
APCApplication Program CommandAPCシーケンスを開始、STで終了\x9F\e_

シーケンスの拡張

  • C1領域の制御コードの中でもシーケンス関連のコードは、それに続く複数のコードを引数にして、さらに機能を拡張する。
DCS = \x1B50
SOS = \x1B58
CSI = \x1B5B
OSC = \x1B5D
PM  = \x1B5E
APC = \x1B5F
  • ESC(\x1B)が制御コードを拡張し、続く2文字目のコードでエスケープシーケンスをさらに拡張している。

CSI(Control Sequence Introducer)

  • 中でも画面出力をコントロールするCSIシーケンス(\e[ = \x1B5B)は重要である。
  • CSIシーケンスを使えば、カーソル位置や文字色・背景色・明るさ・下線・点滅・反転などのテキスト属性を自由に設定できる。

  • CSIシーケンスは、\e[に続けて引数終了文字を指定する。
    • 終了文字が動作を決定する。
    • 引数でその動作の詳細を指定する。

例:

CSI引数終了文字意味
\e[2A2行上にカーソルを移動する
\e[ A1行上にカーソルを移動する(デフォルト値 = 1が利用された)
\e[10;8H10行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カーソル移動(nm列へ)\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
ILn行挿入\e[nL
DLn行削除\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;46m太字・文字色=赤・背景色=水色に設定する(\e[mなどでリセットするまで効果は続く)
\e[4;38;5;150;48;5;200m下線・文字色=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の数値に変換できるのだ。

BGR番号Color
0000Black
0011Red
0102Green
0113黄色Yellow
1004Blue
1015赤紫Magenta
1106水色Cyan
1117White

体感してみる

以上のエスケープシーケンスを理解して、いよいよ体感してみるのだ!

環境と方法
  • iMac 5K OSX 10.10.2
  • ターミナル.app 2.5.1(343.6)
$ 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コマンドを使った方が、無用な悩みを排除できる。
  • ちなみに、echoコマンドを使う場合は、以下の点に注意する。
    • 通常bashでは、bash内部コマンドのechoが実行される。
    • エスケープシーケンスを書く時は、-eオプションが必要になる。
      • と同時に、-nオプションで行末の改行もキャンセルしたくなることが多かった。
    • それでも、\eをエスケープ制御コードと解釈してくれなかった。(なぜだ?)\033を使う必要がある。
256色を体感する

上記SRGの指定に従えば、ターミナル.appは256色を発色する性能を持っている!

  • すべての色を表示してみた。
for c in {0..255}; do printf "\033[48;5;${c}m%8d\033[m" $c; done; echo

f:id:zariganitosh:20150224141919p:image:w450


  • 実は、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

f:id:zariganitosh:20150224142017p:image:w450


  • さらに中間色について、数値優先の並べ方をやめて、色の変化優先の並べ方にしてみる。
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}'

f:id:zariganitosh:20150224142056p:image:w450


これで速やかに、好みの色コードを選択できるようになった!

太字・低輝度・下線・点滅・反転・非表示を体感する
  • リセット(\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

f:id:zariganitosh:20150224143821p:image:w450


  • リセット(\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

f:id:zariganitosh:20150224144000p:image:w450

クルクル回す
  • 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"

f:id:zariganitosh:20150224155226p:image:w450

    • 縦横倍にする時は、上側と下側に分けて2行で表示する仕組みのようだ。
Eで埋める
printf "\e#8"

f:id:zariganitosh:20150224155204p:image:w450

    • 何じゃこれは?各行のフォーマットの状態でも調べるためか?

参考ページ

  • 以下のページがたいへん参考になりました。感謝です!

ISO 2022について


ANSIエスケープシーケンス


VT-100


xterm


エスケープシーケンスの詳細


Linux console_codesについて

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/zariganitosh/20150224/escape_sequence
リンク元