しばたテックブログ

気分で書いている技術ブログです。

PowerShellコンソールの表示色についてのちょっとした話

ちょっと面白いIssueを見つけたので。

github.com

このIssue自体は以下の様に前景色にDarkYellowを指定した場合に表示される色がおかしいというものです。

Write-Host '████████' -ForegroundColor DarkYellow

確かにWindowsで試すと下図の様に黄色ではなく灰色が表示されます。

f:id:stknohg:20160914231324p:plain

PowerShellにおけるコンソールの表示色について

Issue内でも回答されていますが、これはバグではなく仕様です。
PowerShellに限らずコンソールプログラム全般の仕様でもあります。

先ほどの例の様にWrite-Host-ForegroundColor-BackgroundColorで指定できる色はSystem.ConsoleColor型で

メンバー名
0 Black
1 DarkBlue
2 DarkGreen
3 DarkCyan
4 DarkRed
5 DarkMagenta
6 DarkYellow
7 Gray
8 DarkGray
9 Blue
10 Green
11 Cyan
12 Red
13 Magenta
14 Yellow
15 White

の16色あります。

これらの色はPowerShellのプロパティ"画面の色"タブにあるカラーパレットに対応しており、カラーパレットの色を変えるとコンソールの表示色も変わります。

f:id:stknohg:20160914232915p:plain:w480

以下のコードを実行してConsoleColorの各色を表示させてみると、

[System.Enum]::GetValues([System.ConsoleColor]) | % { Write-Host "■" -ForegroundColor $_ -NoNewline }

下図の様になりカラーパレットの色と一致します。

f:id:stknohg:20160914233956p:plain

問題となったDarkYellowは左から7番目のボックスとなり灰色(238,237,240)が設定されてるため名前と実際の色が一致しない事になっています。

ちなみにコマンドプロンプトの場合は黄色(128,128,0)が割り当てられておりPowerShellで(238,237,240)に変更されています。
このほかに6番目のボックスもマゼンタ(128,0,128)から(1,36,86)と変更されています。
どうやら使用頻度の低い色をPowerShell用にカスタマイズしている様です。

PowerShell on Linux(Mac)のコンソール表示色について

とりあえず仕様であることはわかりましたが、これだけではつまらないのでPowerShell on Linux(Mac)についても調べてみます。

例としてUbuntu上のPowerShellで最初のコードを実行すると、以下の図の様になり良い感じの黄色が表示されます。

f:id:stknohg:20160914235506p:plain

当然ですがLinuxやMacには先ほどのカラーパレットはありません。

[System.ConsoleColor]の各色は.NET Coreによって対応するANSIエスケープシーケンスに置き換えられてコンソールに表示されます。

変換テーブルがConsolePal.Unix.cs_consoleColorToAnsiCodeに以下の様に定義されています。

// ConsolePal.Unix.cs より

/// <summary>
/// The values of the ConsoleColor enums unfortunately don't map to the 
/// corresponding ANSI values.  We need to do the mapping manually.
/// See http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
/// </summary>
private static readonly int[] _consoleColorToAnsiCode = new int[]
{
    // Dark/Normal colors
    0, // Black,
    4, // DarkBlue,
    2, // DarkGreen,
    6, // DarkCyan,
    1, // DarkRed,
    5, // DarkMagenta,
    3, // DarkYellow,
    7, // Gray,

    // Bright colors
    8,  // DarkGray,
    12, // Blue,
    10, // Green,
    14, // Cyan,
    9,  // Red,
    13, // Magenta,
    11, // Yellow,
    15  // White
};

DarkYellow3なので最初の例だと

# あくまで変換イメージです
echo -e "\033[33m■■■\033[m"

と変換されるイメージです。

PowerShell ISEのコンソール表示色について

最後にPowerShell ISEについて調べてみます。
PowerShell ISEで最初のコードを試した結果は以下の様になり、こちらも良い感じの黄色が表示されます。

f:id:stknohg:20160915001935p:plain

PowerShell ISEでは[System.ConsoleColor]の各色を独自の変換テーブルで[System.Windows.Media.Color]に変換します。
変換テーブルはMicrosoft.PowerShell.GPowerShell.dllMicrosoft.Windows.PowerShell.Gui.Internal.GPSHostRawUserInterfaceクラスに定義されており、残念ながらこのクラスはオープンソース化していないためILSpy等で中身を見てみると以下の様になっていることがわかります。

// Microsoft.Windows.PowerShell.Gui.Internal.GPSHostRawUserInterface より

internal static readonly Color[] ConsoleColors = new Color[]
{
    Colors.Black,
    Colors.DarkBlue,
    Colors.DarkGreen,
    Colors.DarkCyan,
    Colors.DarkRed,
    Colors.DarkMagenta,
    Color.FromRgb(170, 170, 0),
    Colors.DarkGray,
    Colors.Gray,
    Colors.Blue,
    Colors.Lime,
    Colors.Cyan,
    Colors.Red,
    Colors.Magenta,
    Colors.Yellow,
    Colors.White
};

ここからDarkYellowは(170,170,0)に変換されることがわかります。
他の色は定義済みの色なのでそのままですね。