かつて山市良と呼ばれたおじさんのブログ

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  メモ. WSLの実行状態をPowerShellで取得しようとしたら不可視文字に阻まれた話

 

 

メモ. WSLの実行状態をPowerShellで取得しようとしたら不可視文字に阻まれた話

2026年01月16日配信
執筆者:山内 和朗

 WindowsでLinuxシェルを動かすことができる「Windows Subsystem for Linux(WSL)」は、wslコマンド(C:¥windows¥system32¥wsl.exe)を使用して開始したり、停止したり、実行状態を確認したりできます。wslコマンドはコマンドプロンプト(cmd.exe)やWindows PowerShell(pwershell.exe)、PowerShell 7.x(pwsh.exe)から実行できますが、wslの操作をPowerShellのスクリプトに組み込みたい場合はちょっとした工夫が必要です。外部ツールの標準出力をPowerShellで扱う際によくある嵌りどころだと思うのでメモとして残しておきます。

 

WSLコマンドの出力にあるはずの文字列がマッチしない謎

 

 以下の記事で紹介したWSLのポートフォワード(ポートプロキシ)設定を自動化するスクリプトに、WSLの実行状態に応じてWSLを開始する処理を追加したいと考えました。以下の記事では単純にバックグラウンドで開始するコードを最初に記述してありますが、それに必要があれば開始するというロジックを組み込みたいというのがきっかけです。

vol.23 ラボ環境 on Azureに、共通のテスト用メール環境を導入したい

 WSLのLinuxディストリビューションの実行状態は、以下のコマンドラインを実行することで確認できます。

wsl -l -v(またはwsl --list --verbose)

 

 このwslコマンドの出力結果をPowerShellで取得して、Containsメソッドや-contains、-match、-like比較演算子などで特定の文字列を検索しようとしても、1文字なら一致する行が見つかるのに、2文字以上になるとマッチしないという状況に遭遇しました(画面1)。PowerShellの変数$linesに格納した出力結果は単純にwslコマンドを実行したときには見えない空行があります。この空行も気になるところですが簡単に省けるのでとりあえず無視します。

 

画面1 2行目には"Ubuntu"の文字列が存在するはずなのに、複数文字ではマッチしない
画面1 2行目には"Ubuntu"の文字列が存在するはずなのに、複数文字ではなぜかマッチしない

 最初は何が何だかわからず混乱して無駄に時間を浪費してしまいました。そこで初心に戻り、wslコマンドの出力結果を1文字ずつ配列に格納して、その文字コードを確認してみたところ、<0x00>U<0x00>b<0x00>u<0x00>n<0x00>t<0x00>u<0x00>のように、すべての文字とスペース(<0x20>)の前後にNULや制御文字のような不可視文字が存在することが分かりました(画面2)。これは、外部ツールの出力をPowerShellで扱う際にまれに遭遇します(他に同様のツールはないかと問われれば、忘れてしまいましたが、Windows Sysinternalsの一部のツールがそうだったように記憶しています)。wslコマンドの出力はUTF-16LE(Windows APIの標準)であり、PowerShellはそれをPowerShell標準のUTF-8前提で処理してしまうため、余計な0x00(UTF-16LEの下位ビット)が混入しているように見える(表示上は見えない)のです。先ほど無視した余計な空行(余計な改行)もその影響です。

 

画面2 1文字ずつ文字コードを確認してみると、すべての可視文字とスペースの間に
画面2 1文字ずつ文字コードを確認してみると、すべての可視文字とスペースの間に

 ここまで分かればもう対処は可能です。wslコマンドの出力をファイルにリダイレクトし、それを読み込む際に文字コード(UTF-16LEはUnicode)を指定してあげれば、UTF-8として期待通りに文字列を評価できるようになります(画面3)。

 

memo60_scr03b

画面3 wslコマンドの出力をテキストファイル(UTF-16LE)にリダイレクトし、文字コード指定で読み込むことでUTF-8として期待通りに文字列を評価できる

 

 ファイルを介さずに処理するには、.NETのProcessStartInfoクラス(Microsoft Learn)を使用して、StandardOutputEncodingでUnicodeを指定して標準出力を読み取ればよいようですが、どうしてもコードが長くなってしまいます(画面4、出力結果がUTF-16LEのツールを実行し、UTF-8で結果を取得するInvoke-ExternalTool関数のサンプルコード)。

 

memo60_scr04b

画面4 .NETのProcessStartInfoクラスを使用してファイルを介さずに対処する例。どうしてもコードが長くなってしまう(サンプルコード

 

 wsl -l -vの結果など対象が簡単なものであれば、<0x00>を置換して取り除くという簡易的な方法で疑似的にUTF-8にする方法があります。それにには、「¥p{C}+」(NULを含む制御文字)または「¥p{Cc}+」(NULを含む制御文字)または「¥x00」(NUL)を""に置換します(画面5)。この簡易的な方法であれば、既に作成済みのスクリプトに最小限の変更だけで問題に対処できます。

 

画面3 wslコマンドの出力結果から「¥x00」を置換して取り除き疑似的にUTF-8にすれば、コードの変更を最小限にして対処可能
画面5 wslコマンドの出力結果から「¥x00」を置換して取り除き疑似的にUTF-8にすれば、コードの変更を最小限にして対処可能

 

WSLの状態を取得するための汎用カスタム関数

 

 次のGet-WslStatus関数は、WSLの特定のLinuxディストリビューションの詳細情報を返すカスタム関数です。文字列検索の問題に気付く前にほとんどコード化してしまっていたため、「¥x00」を置換する簡易的な方法で対応しました。

 

 Get-WslStatusをパラメーターなしで実行すると、既定のLinuxディストリビューション(wsl -l -vで*が付いているもの)の詳細情報を返します。詳細情報には、名前(Name)、状態(Status)、WSLバージョン(Version)、既定のLinuxディストリビューションであるかどうか(IsDefault、True/False)の4つの値を含むオブジェクトを返します(画面6)。


画面6 Get-WslStatus関数は、特定のLinuxディストリビューションの詳細情報を返す
画面6 Get-WslStatus関数は、特定のLinuxディストリビューションの詳細情報を返す

 

Get-WslStatus関数(プレーンテキストで表示)

function Get-WslStatus {
    param (
      [string]$Name = "*" #既定のLinuxディストリビューション(ワイルドカードではない)
    )
    $Status = "unknown"
    $offset = 0
    $default = $false
    $lines = wsl.exe -l -v | Out-String -Stream
    #$lines = $lines | ForEach-Object { $_ -replace '\p{Cc}+', '' }
    $lines = $lines | ForEach-Object { $_ -replace '\x00', '' }
    foreach ($line in $lines) {
      if ([string]::IsNullOrWhiteSpace($line)) { continue }
      if ($line -match '^\s*NAME\s+STATE\s+VERSION\s*$') { continue }
      if ($line.ToLower().Contains($Name.ToLower())) {
        $line = $line -split "\s+" |Where {$_ -ne ""}
        if ($line.Count -eq 3) {
          $offset = -1
        } elseif (($line.Count -eq 4) -and $line.Contains("*")) {
          $default = $true
        }
        return [pscustomobject]@{
            Name   = $line[1+$offset]
            Status = $line[2+$offset]
            Version = $line[3+$offset]
            IsDefault   = $default
        }

      } 
    }
    Write-Error "$($Name) は見つからないか、状態が不明です。"
}
# Usage:
# Get-WslStatus "Linuxディストリビューション名(省略または * は既定のディストリビューション)"
Get-WslStatus


 Get-WslStatus関数をPowerShellスクリプトに組み込むと、特定のLinuxディストリビューションの実行を簡単に制御することができます。例えば、次のコードは、Linuxディストリビューション「Ubuntu」が停止状態であれば開始し、何かキーを押すと終了します。

 

function Get-WslStatus {
  #省略
}
$style = "Hidden" #Hiden(バックグラウンド)またはNormal(フォアグラウンド)
$ret = Get-WslStatus "Ubuntu"
if ($ret.Status -eq "Stopped") {
  Write-Host "WSL ($($ret.Name)) を開始します。 ..."
  Start-Process wsl.exe -ArgumentList "-d $($ret.Name)" -WindowStyle $style
} elseif ($ret.Status -eq "Running") {
  Write-Host "WSL $($ret.Name) は既に実行中です。"
}
sleep 5
$ret = Get-WslStatus "Ubuntu"
$ret
pause
if ($ret.Status -eq "Running") {
  Write-Host "WSL ($($ret.Name)) を終了します。..."
  wsl.exe --terminate $ret.Name
}


 当初の目的のポートフォワード用のスクリプトについては上記の例のようなコードを最初と最後に入れて自動化しました。修正版のスクリプトは長くなってしまいましたので省略します。

 

blog_yamanxworld_subscribe

blog_yamanxworld_comment

blog_yamanxworld_WP_ws2025

最新記事