PowerShell で ANSI escape sequences を表示する

# 背景
PowerShell 上で Packer や Terraform を使うとき、出力に ANSI escape sequences が含まれるがデフォルトコンソールではこれを解釈できません。ツール側で色付けを無効化（例: Packer の `PACKER_NO_COLOR=true` や Terraform の `-no-color`）すれば回避できますが、せっかくなら色付きで出力して欲しいです。

# 方法1 : ansicon を使う
[ansicon](https://github.com/adoxa/ansicon) は元々 cmd.exe 向けの ANSI escape sequences を表示するためのアプリケーションです。コマンドプロンプトから `ansicon` と入力すると、 ansicon を経由した cmd.exe が立ち上がり、ANSI escape sequences を解釈してくれるようになります。これを PowerShell で使います。

![ansicon サンプル](https://qiita-image-store.s3.amazonaws.com/0/58889/7800476f-f4e0-4ab9-0d93-21e04624132b.png)

※サンプルの `ANSI Prompt Colours.txt` は[こちらの ANSI Prompt Colours (25k)](http://adoxa.altervista.org/ansicon/) です。

## インストール
```
choco install ansicon
```

または [GitHub](https://github.com/adoxa/ansicon/releases) からバイナリをダウンロードして適当な場所に置いてください。

## 使い方
```
ansicon powershell
```

で ansicon 経由で powershell が起動できます。

![ansicon サンプル on PowerShell](https://qiita-image-store.s3.amazonaws.com/0/58889/0c78da7a-fc2a-b39f-4640-0dea8030ade1.png)

ただし、見ての通り Dark Yellow が白に、 Dark Magenta が背景色になっており、おかしいです。直し方は今のところ分かっていません。

## NVIDIA のビデオカードを使用していてエラーが出る場合
NVIDIA のドライバと競合します。回避するためには環境変数に以下を追加します。

```
ANSICON_EXC=nvd3d9wrap.dll;nvd3d9wrapx.dll
```

一時的に環境変数に追加した場合は以下の通りです。

```
$env:ANSICON_EXC = "nvd3d9wrap.dll;nvd3d9wrapx.dll"
```

これを設定しない場合、例えば [posh-git](https://github.com/dahlbyk/posh-git) で git.exe を呼び出そうとしたときに git.exe がフリーズします。

# 方法 2 : コンソールエミュレータを使う
以下のような高機能コンソールエミュレータは ANSI escape sequences を解釈してくれます。他にもいろいろな機能があるので、普通はこっちの方法で解決するのがお勧めだと思います。私はデフォルトのコンソールが使いたいのと、コンソールエミュレータは重いので前述の ansicon を必要に応じて使っています。

　　-　[ConEmu](https://github.com/Maximus5/ConEmu)
　　-　[ConsoleZ](https://github.com/cbucher/console)

なお、[Console2](http://sourceforge.net/projects/console/files/) のように ANSI escape sequences に対応していないコンソールエミュレータでも、前述の ansicon と組み合わせれば対応可能です（[参考](https://github.com/adoxa/ansicon/issues/13)）。

# 方法 3 : PowerShell スクリプトでなんとかする
原始的な方法ですが PowerShell スクリプトで正規表現などを用いて ANSI escape sequences を PowerShell での色表現に置き換えれば外部アプリケーションを用いずに対応できるはずです。[実際にやっている人がいました](http://blog.fastconnect.fr/?tag=powershell)。

ちょっとだけ手直ししたコードを以下に記載します。

```Write-ColoredHost.ps1
function global:Write-ColoredHost
{
    Param(
        [switch]$noNewLine,
        [String]$separator=" ",
        [System.ConsoleColor]$foregroundColor = $Host.UI.RawUI.ForegroundColor,
        [System.ConsoleColor]$backgroundColor = $Host.UI.RawUI.BackgroundColor
    )
    BEGIN {
        $fgc, $bgc = $foregroundColor, $backgroundColor
    }
    PROCESS {
        $token, $coloredTokens = $_ -split "\e\["
        Write-Host $token `
                   -noNewLine -separator $separator `
                   -foregroundColor $fgc `
                   -backgroundColor $bgc
        foreach ($coloredToken in $coloredTokens) {
            $colorCodes, $token = $coloredToken -split "m", 2
                $colorCodes -split ";" | foreach-object {
                    switch -regex ($_) {
                        "^0{1,2}$" { $fgc, $bgc = $foregroundColor, $backgroundColor }
                        "^30$"     { $fgc       = "Black"                            }
                        "^31$"     { $fgc       = "Red"                              }
                        "^32$"     { $fgc       = "Green"                            }
                        "^33$"     { $fgc       = "Yellow"                           }
                        "^34$"     { $fgc       = "Blue"                             }
                        "^35$"     { $fgc       = "Magenta"                          }
                        "^36$"     { $fgc       = "Cyan"                             }
                        "^37$"     { $fgc       = "White"                            }
                        "^39$"     { $fgc       = $foregroundColor                   }
                        "^40$"     {       $bgc =                   "Black"          }
                        "^41$"     {       $bgc =                   "Red"            }
                        "^42$"     {       $bgc =                   "Green"          }
                        "^43$"     {       $bgc =                   "Yellow"         }
                        "^44$"     {       $bgc =                   "Blue"           }
                        "^45$"     {       $bgc =                   "Magenta"        }
                        "^46$"     {       $bgc =                   "Cyan"           }
                        "^47$"     {       $bgc =                   "White"          }
                        "^49$"     {       $bgc =                   $backgroundColor }
                    }
                }
            Write-Host $token `
                       -noNewLine -separator $separator `
                       -foregroundColor $fgc `
                       -backgroundColor $bgc
        }
        if (!$noNewLine) { Write-Host }
    }
}
```

これを適当なフォルダに置いて

```
.\Write-ColoredHost.ps1
Get-Content '.\ANSI Prompt Colours.txt' | Write-ColoredHost
```

のようにすると色付きで表示されます。ただし、一部の ANSI escape sequences にしか対応していないため「とりあえず近い色が付く」程度のものになっています。

![Write-ColoredHost　サンプル](https://qiita-image-store.s3.amazonaws.com/0/58889/03061299-c696-657f-4231-39ac963af30b.png)

PowerShell にこの関数を組み込みたい場合は[このあたり](http://codezine.jp/article/detail/2623)を参考にしてください。