1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windows Script Host (WSH) でカラー出力する方法

Last updated at Posted at 2025-09-07

Windows Terminal なら余裕。

はじめに

Windows で標準で使えるスクリプト言語と言えば

  • コマンドプロンプトのバッチファイル
  • Windows Script Host (WSH) の JScript
  • Windows PowerShell(PowerShell 5.1系)

の3つだと思います。厳密には WSH では VBScript も使えますが,非推奨になっているので新たに作るものでは VBScript を使わないほうが良いでしょう1

で,これらのスクリプト環境でカラー出力したいときがあります。

このうち PowerShell はカラー出力に対応しています。バッチファイルは VT100 準拠のエスケープシーケンスを利用してカラー出力可能です2。ところが WSH ではエスケープシーケンスを通してくれないのでカラー出力できないのです。

プログラミング言語としてはバッチファイルや PowerShell スクリプトよりも JScript のほうが遥かに親しみ易いと思うので,可能な限り WSH を使いたい方も多いでしょう。

ということで WSH でなんとかカラー出力できないか悪戦苦闘した記事です。

PowerShell スクリプトの場合

まずは PowerShell スクリプトの例を見てみます。PowerShell の場合,色を名前で指定できるので非常に分かり易いですね3。なお,背景色もオプション -BackgroundColor で指定できます。ちなみに色の順番は後述するエスケープシーケンスの番号に合わせています。

test.ps1
Write-Host -ForegroundColor darkred     '暗い赤色'
Write-Host -ForegroundColor darkgreen   '暗い緑色'
Write-Host -ForegroundColor darkyellow  '暗い黄色'
Write-Host -ForegroundColor darkblue    '暗い青色'
Write-Host -ForegroundColor darkmagenta '暗い紫色'
Write-Host -ForegroundColor darkcyan    '暗い水色'
Write-Host -ForegroundColor gray        '灰色'
Write-Host -ForegroundColor darkgray    '暗い灰色'
Write-Host -ForegroundColor red         '赤色'
Write-Host -ForegroundColor green       '緑色'
Write-Host -ForegroundColor yellow      '黄色'
Write-Host -ForegroundColor blue        '青色'
Write-Host -ForegroundColor magenta     '紫色'
Write-Host -ForegroundColor cyan        '水色'
Write-Host -ForegroundColor white       '白色'
Write-Host                              'デフォルト'

PowerShell スクリプトの実行結果を示します。

コマンドプロンプトのバッチファイルの場合

一方,コマンドプロンプトのバッチファイルですが,VT100 準拠のエスケープシーケンスを使用すればカラー出力可能です4。なお,テキストエディタによってはエスケープコード ^[(16進数で1Bh)を入力・表示するのに苦労するかもしれません。

バッチファイルの実行結果を示します。

Windows Script Host (WSH) の JScript の場合

JScript の例を示します。JScript ではエスケープコードを \x1b と書けるのが良いですね。

test.js
WScript.Echo("\x1b[31m暗い赤色");
WScript.Echo("\x1b[32m暗い緑色");
WScript.Echo("\x1b[33m暗い黄色");
WScript.Echo("\x1b[34m暗い青色");
WScript.Echo("\x1b[35m暗い紫色");
WScript.Echo("\x1b[36m暗い水色");
WScript.Echo("\x1b[37m灰色");
WScript.Echo("\x1b[90m暗い灰色");
WScript.Echo("\x1b[91m赤色");
WScript.Echo("\x1b[92m緑色");
WScript.Echo("\x1b[93m黄色");
WScript.Echo("\x1b[94m青色");
WScript.Echo("\x1b[95m紫色");
WScript.Echo("\x1b[96m水色");
WScript.Echo("\x1b[97m白色");
WScript.Echo("\x1b[0mデフォルト");

ただし,カラー出力できません。うまくエスケープシーケンスを解釈してくれないようです。

ためしに出力をファイルに一旦リダイレクトしてから表示してみると上手くカラー表示できるので,エスケープシーケンス自体は正常に出力されており,エスケープシーケンスが解釈されないでスルー出力されていると思われます。

コンソールモードを調べる

Windows 10 以降,VT100 準拠のエスケープシーケンスがサポートされたとはいえ,デフォルトでは有効になっていない可能性があります。そこで下記のC言語プログラムを作成して確認することにしました。

ESCAPE.C
#include <windows.h>
#include <stdio.h>
#include <string.h>
int	main( int argc, char *argv[] ) {
//	HANDLE	handle = GetStdHandle( STD_OUTPUT_HANDLE );
	HANDLE	handle = CreateFile( "CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	DWORD	mode;
	GetConsoleMode( handle, &mode );
	if( argc < 2 ) {
		fprintf( stderr, "エスケープシーケンスは %s です。\n",
			( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ? "<ON>" : "<OFF>" );
		return 0;
	} else if( !_stricmp( argv[1], "ON" ) ) {
		mode |=  ENABLE_VIRTUAL_TERMINAL_PROCESSING;
	} else if( !_stricmp( argv[1], "OFF" ) ) {
		mode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
	} else {
		fprintf( stderr, "エスケープシーケンスの ON と OFF を切り替えます。\n" );
		fprintf( stderr, "\n" );
		fprintf( stderr, "ESCAPE(.EXE) [ON | OFF]\n" );
		return -1;
	}
	SetConsoleMode( handle, mode );
	CloseHandle( handle );
	return 0;
}

ビルド方法は cl コマンド一発です。Visual Studio Community 2022 の 32bit 版Cコンパイラを使いました。

ビルド方法
cl ESCAPE.C

引数なしで実行すると現在の設定値を表示します。やはり,デフォルトではエスケープシーケンス機能が OFF 状態になっているようです。

現在のエスケースシーケンスの設定を確認する
c:\Qiita>escape
エスケープシーケンスは <OFF> です。

パラメータを指定すると ON/OFF の設定を切り替えられます。残念ながら,プログラムが終了すると OFF 状態に戻ってしまうようです。

エスケープシーケンスを ON にできるか?
c:\Qiita>escape on

c:\Qiita>escape
エスケープシーケンスは <OFF> です。

解決法その1

先ほど作成したエスケープシーケンスを有効化させるプログラムを WSH の実行中に呼び出します。

test2.js
var	shell = WScript.CreateObject("WScript.Shell");
var	proc = shell.Exec("ESCAPE ON");
while(proc.Status == 0) WScript.Sleep(100);
WScript.Echo("\x1b[31m暗い赤色");
WScript.Echo("\x1b[32m暗い緑色");
WScript.Echo("\x1b[33m暗い黄色");
WScript.Echo("\x1b[34m暗い青色");
WScript.Echo("\x1b[35m暗い紫色");
WScript.Echo("\x1b[36m暗い水色");
WScript.Echo("\x1b[37m灰色");
WScript.Echo("\x1b[90m暗い灰色");
WScript.Echo("\x1b[91m赤色");
WScript.Echo("\x1b[92m緑色");
WScript.Echo("\x1b[93m黄色");
WScript.Echo("\x1b[94m青色");
WScript.Echo("\x1b[95m紫色");
WScript.Echo("\x1b[96m水色");
WScript.Echo("\x1b[97m白色");
WScript.Echo("\x1b[0mデフォルト");

こうするとカラー出力が可能です。

解決法その2

エスケープシーケンスを有効化させるプログラムを起動し,そのまま WSH のプログラムを子プロセスとして実行させます。

VT100ON.C
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <errno.h>
int	main( int argc, char *argv[] ) {
	//--------------------------------------------------------------------------
	// ヘルプメッセージ
	//--------------------------------------------------------------------------
	if( argc < 2 ) {
		fprintf( stderr, "エスケープシーケンスを ON にしてコマンドを実行します。\n" );
		fprintf( stderr, "\n" );
		fprintf( stderr, "VT100ON(.EXE) [コマンド] [オプション...]\n" );
		return -1;
	}
	//--------------------------------------------------------------------------
	// エスケープシーケンスを有効化
	//--------------------------------------------------------------------------
//	HANDLE	handle = GetStdHandle( STD_OUTPUT_HANDLE );
	HANDLE	handle = CreateFile( "CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	DWORD	mode;
	GetConsoleMode( handle, &mode );
	mode |=  ENABLE_VIRTUAL_TERMINAL_PROCESSING;
	SetConsoleMode( handle, mode );
	CloseHandle( handle );
	//--------------------------------------------------------------------------
	// コマンドラインパラメータを引き継いで子プロセスを起動
	//--------------------------------------------------------------------------
	int	ret = (int)_spawnvp( _P_WAIT, argv[1], &argv[1] );
	//--------------------------------------------------------------------------
	// エラーメッセージの表示
	//--------------------------------------------------------------------------
	char	*msg = (char*)NULL;
	switch( errno ) {
	  case E2BIG:	msg = "引数リストが 1024 バイトを超えています。";				break;
	  case EINVAL:	msg = "mode 引数が無効です。";								break;
	  case ENOENT:	msg = "ファイルまたはパスが見つかりません。";					break;
	  case ENOEXEC:	msg = "指定されたファイルが実行可能ファイルではないか、"
						  "無効な実行可能ファイル形式です。";						break;
	  case ENOMEM:	msg = "新しいプロセスを実行するのに十分なメモリがありません。";	break;
	}
	if( msg != (char*)NULL ) fputs( msg, stderr );
	return ret;
}

ビルド方法は cl コマンド一発です。

ビルド方法
cl VT100ON.C

この場合もカラー出力が可能です。なお,注意事項として WSH のインタプリタ cscript.exe を明示して呼び出す必要があります。

解決法その3

先ほど作成したプログラム VT100ON.EXE を使ってコマンドインタープリタ CMD.EXE を子プロセスとして実行します。幸いなことにコマンドインタープリタはエスケープシーケンスの設定を親プロセスから引き継ぐようなのでデフォルトで ON 状態になっています。こうなれば,WSH でカラー出力が可能です。

解決法その4

コマンドプロンプトの起動直後からエスケープシーケンスが有効になるようなレジストリ設定があるといいなと思ってずぅぅぅっとネット検索を続けていたら,ようやく見つかりました。下記レジストリを追加すれば良いのです56

表1 エスケープシーケンスを有効にするレジストリ
キー名 値名
HKCU\Console VirtualTerminalLevel REG_DWORD 0x1

ただし,レジストリを追加した直後から使える訳ではありません。

いったんコマンドプロンプトを終了して,もう一度立ち上げ直すとレジストリの変更が有効になるようです。

解決法その5

エスケープシーケンスを有効にするC言語プログラムを作成したり,レジストリを書き換える必要がない方法です。標準出力ではなく,コンソール出力のデバイス CONOUT$ をオープンして書き込むという方法です。

test3.js
var	fs = WScript.CreateObject("Scripting.FileSystemObject");
var	ts = fs.CreateTextFile("CONOUT$");
ts.WriteLine("\x1b[31m暗い赤色");
ts.WriteLine("\x1b[32m暗い緑色");
ts.WriteLine("\x1b[33m暗い黄色");
ts.WriteLine("\x1b[34m暗い青色");
ts.WriteLine("\x1b[35m暗い紫色");
ts.WriteLine("\x1b[36m暗い水色");
ts.WriteLine("\x1b[37m灰色");
ts.WriteLine("\x1b[90m暗い灰色");
ts.WriteLine("\x1b[91m赤色");
ts.WriteLine("\x1b[92m緑色");
ts.WriteLine("\x1b[93m黄色");
ts.WriteLine("\x1b[94m青色");
ts.WriteLine("\x1b[95m紫色");
ts.WriteLine("\x1b[96m水色");
ts.WriteLine("\x1b[97m白色");
ts.WriteLine("\x1b[0mデフォルト");

ただし,そのままではカラー出力できません。標準出力を NUL デバイスにリダイレクトすると何故かカラー出力できるのです。

これで何故カラー出力できるのか?詳しい動作原理は分かっていません。仮にカラー表示できるのなら,NULデバイスにリダイレクトしなくてもカラー表示できて欲しいところです。この方法は,標準出力がファイルにリダイレクトされているかどうか判別する方法を模索しているときに偶然発見したものです。標準出力がファイルにリダイレクトされているときにはエスケープシーケンスを付けないようにするのが目的でした。リダイレクトの判別方法自体はまだ発見できていませんが,おそらく不可能だと考えています。

Windows Terminal ならどうなの?

ちなみに Windows Terminal ではデフォルトでエスケープシーケンスが ON になっていました。ということで,Windows Terminal であれば特に技巧を凝らさなくてもエスケープシーケンスを通してくれるのでカラー出力が可能です。

結論

  • Windows 10 以降,VT100 準拠のエスケープシーケンスがサポートされ,コンソールプログラムでもエスケープシーケンス利用したカラー出力が可能になりました。ただし,デフォルトではエスケープシーケンスは OFF 状態になっています。このため,コマンドプロンプト上で動作するプログラムはエスケープシーケンスを有効化させるための Win32 API を呼び出す必要がありますが,当該プログラムが終了するとエスケープシーケンスの設定は元に戻ります。つまり,エスケープシーケンスの有効期間は ON にしたプログラムの実行中に限ります。
  • ただし,バッチファイルでは何もしなくてもエスケープシーケンスを利用可能です。おそらく echotype などのビルトインコマンドは内部的にエスケープシーケンスを ON にして実行しているものと推察されます。
  • Windows Script Host(WSH)でエスケープシーケンスを利用する場合,下記5つの方法があります。
    その1)WSH スクリプト実行中にエスケープシーケンス有効化プログラムを実行
    エスケープシーケンスを ON にするプログラムを別途作成し,WSH のスクリプト内部から呼び出します。エスケープシーケンスを有効にするプログラムの実行が終了しても,コマンドインタプリタに処理を返さなければエスケープシーケンスの ON 状態を継続できるようです。
    その2)エスケープシーケンス有効化プログラムで WSH スクリプトを実行
    エスケープシーケンスを ON にするプログラムを別途作成し,そのまま子プロセスで WSH のスクリプトを実行するようにします。本記事では敢えてコマンドインタプリタを介さない spawn 系の関数を使いましたが,コマンドインタプリタ自身はエスケープシーケンス設定を親プロセスから引き継ぐことが明らかになったので,コマンドインタプリタを介する system 関数でも良さそうです。
    その3)エスケープシーケンス有効化プログラムでコマンドインタプリタを起動
    エスケープシーケンスを ON にするプログラムを別途作成し,そのまま子プロセスでコマンドインタプリタ CMD.EXE を起動します。このコマンドインタプリタは親プロセスの設定を継承してエスケープシーケンスが有効になっているのでカラー出力可能です。
    その4)レジストリ書き換え
    下記レジストリを追加します。残念ながらマイクロソフトから発信された公式情報を確認できませんでした。一番確かそうな情報が夏休みにマイクロソフトに来たインターン生由来って一抹の不安を感じるのは筆者だけでしょうか。
    キー名 値名
    HKCU\Console VirtualTerminalLevel REG_DWORD 0x1
    その5)コンソール出力デバイスに書き込む
    コンソール出力デバイス CONOUT$ をオープンして書き込みます。ただし,そのままではカラー出力できません。何故か標準出力を NUL デバイスにリダイレクトするとカラー出力できるようになります。ちなみに詳しい動作原理は分かっていません。
  • Windows Terminal はデフォルトでエスケープシーケンスが ON 状態になっているので,とくに何の技巧を凝らすことなく普通にカラー出力可能です。

$\Huge \color{red}{\textsf{もうみんな Windows Terminal を使っちゃいなよ!!}}$

失敗または隘路事項

標準出力のハンドルを得るコードを探すと下記の例がよく見つかります。

標準出力のハンドルを得る
HANDLE	handle = GetStdHandle( STD_OUTPUT_HANDLE );

しかしながら,本記事の場合,直接コンソール出力のハンドルを得る下記のコードではないとうまく動作しません。

コンソール出力のハンドルを得る
HANDLE	handle = CreateFile( "CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL,
	OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

付録

コマンドプロンプトのデフォルトカラーと PowerShell におけるカラー名称,エスケープシーケンスコードおよびカラー名称の対応表を以下に示します。

ややこしいのは「Gray」が「白」で「DarkGray」が「明るい黒」あたりでしょうか。

表2 カラー対応表
デフォルト
カラー
PowerShell
カラー名称
エスケープシーケンス
コード カラー名称
#0C0C0C Black ESC [30m
#C50F1F DarkRed ESC [31m
#13A10E DarkGreen ESC [32m
#C19C00 DarkYellow ESC [33m 黄色
#0037DA DarkBlue ESC [34m
#881798 DarkMagenta ESC [35m マゼンタ
#3A96DD DarkCyan ESC [36m シアン
#CCCCCC Gray ESC [37m
#767676 DarkGray ESC [90m 明るい黒
#E74856 Red ESC [91m 明るい赤
#16C60C Green ESC [92m 明るい緑
#F9F1A5 Yellow ESC [93m 明るい黄色
#3B78FF Blue ESC [94m 明るい青
#B4009E Magenta ESC [95m 明るいマゼンタ
#61D6D6 Cyan ESC [96m 明るいシアン
#F2F2F2 White ESC [97m 明るい白
  1. VBScript はこうして生まれた。- Qiita

  2. Windows向けのプログラムでANSIエスケープシーケンスを使うには - Qiita

  3. PowerShellメモ Write-Host色一覧 - Qiita

  4. コンソール仮想ターミナルシーケンス - microsoft

  5. Windows コマンド プロンプトやPowerShellでANSIエスケープシーケンスを有効にする - Zenn

  6. Understanding Windows Console Host Settings - Microsoft Dev Blogs

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?