😽

Windows × WSL 自動化の正解

に公開
2025/12/07

Windows と Linux (WSL) がこれほどスムーズに共存する時代になっても、ひとたび「自動化」や「連携」を行おうとすると、私たちは見えない壁にぶつかります。

  • 「手動で叩くと動くのに、タスクスケジューラだと動かない」
  • 「PowerShell から WSL のファイルが見つからないと言われる」
  • 「Python の venv がパスエラーで壊れる」

これらは個別のバグではなく、「Windows と WSL の境界線」に対する理解不足(あるいは OS の仕様上の限界)が原因です。

この記事では、数々の失敗とエラーログの果てにたどり着いた、「Windows × WSL 環境における自動起動と連携のベストプラクティス」を共有します。

特定のツールに限らず、あらゆるスクリプトに応用できる普遍的な「設計パターン」です。


前提:なぜ自動化は失敗するのか?

まず、敵(エラーの原因)を知りましょう。9割の失敗は以下の2点に集約されます。

1. UNCパス(\\wsl.localhost\...)の脆弱さ

WSL のファイルシステムは、Windows 側からはネットワークパス(UNCパス)として見えています。

\\wsl.localhost\Ubuntu-22.04\home\user\project

しかし、Windows の多くのレガシーツール(CMDなど)や一部のランタイム(Pythonなど)は、この UNC パスを正しく扱えません。
カレントディレクトリに設定できなかったり、パスの解決に失敗したりします。これが「手動なら動くのに……」の正体です。

2. コンテキストの乖離

  • WSL: 開発環境(Python, Node, Dockerなどが動く)
  • Windows: 実行環境(タスクスケジューラ、GPUドライバ、GUI)

これらは別世界です。WSL の中の .bashrc に書いた環境変数は、Windows のタスクスケジューラには届きません。
「Windows 側で自動起動したいなら、Windows の作法で環境を整える」必要があります。


解決策:アーキテクチャの全体像

安定して動作する「正解」の構成は以下の通りです。

  1. UNC 禁止: WSL のパスを Windows のドライブレター(Z: など)にマッピングする。
  2. 役割分担: 「開発」は WSL で行い、「起動」のトリガーは Windows 側のローカルファイルに置く。
  3. 起動元: スタートアップフォルダではなく、タスクスケジューラを使う。

この構成を作るための具体的なステップを解説します。


Step 1: 「UNC」を捨てて「ドライブ」にマッピングする

これが最大のポイントです。不安定な \\wsl.localhost\... を使うのをやめ、仮想ドライブとしてマウントします。

スクリプト内で動的にマウントすることで、パス問題を完全に回避できます。

run_script.ps1
# 1. ネットワークドライブとして割り当てられていなければマウントする
if (!(Test-Path Z:)) {
    # WSLのルートを Zドライブに割り当て(永続化はしない)
    New-PSDrive -Name "Z" -PSProvider FileSystem -Root "\\wsl.localhost\Ubuntu-22.04\home\user" -ErrorAction SilentlyContinue
}

# 2. Zドライブ上のスクリプトとして実行する
Set-Location Z:\project
# これで Windows から見ても "Z:\project" というローカルなパスとして扱える!

Step 2: ブートストラップ(起動準備)スクリプトを作る

「いきなり Python や Node を叩く」のではなく、「環境を整えてから叩く」ための PowerShell スクリプト(ブートストラップ) を一層挟みます。

WSL 側からこのスクリプトを生成・配置するのがスマートです。

setup_windows_task.sh
#!/bin/bash

# WSL内のパスを Windows形式に変換(例: Z:\project)
# ※ ここではあえてドライブレター前提で書くか、あとで置換する設計にする
WIN_PROJECT_DIR="Z:\\project"

# Windows側の起動用スクリプトを作成
cat <<EOF > run_task.ps1
# エラーで止まらないように設定
\$ErrorActionPreference = "Stop"

# ドライブマウント(前述のコード)
if (!(Test-Path Z:)) {
    New-PSDrive -Name "Z" -PSProvider FileSystem -Root "\\\\wsl.localhost\\Ubuntu-22.04\\home\\user"
}

# ディレクトリ移動
Set-Location "$WIN_PROJECT_DIR"

# アプリケーションの実行(Windows側のPythonを使う例)
# フルパス指定が安全
& "C:\\Python311\\python.exe" main.py
EOF

echo "Windows用起動スクリプトを生成しました: run_task.ps1"

Step 3: タスクスケジューラへの登録(唯一の正解)

「スタートアップフォルダ」へのショートカット配置は、現代の Windows では推奨しません。起動タイミングの制御ができず、権限周りのトラブルが多いからです。

タスクスケジューラ を使うのが最も確実です。

推奨設定

手動で設定しても良いですが、PowerShell から一発で登録するコマンドを知っておくと便利です。

# 管理者権限の PowerShell で実行
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File C:\Path\To\run_task.ps1"
$Trigger = New-ScheduledTaskTrigger -AtLogOn
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries

# タスク登録
Register-ScheduledTask -Action $Action -Trigger $Trigger -Settings $Settings -TaskName "MyWSLAppAutoStart" -User "MyWindowsUser" -RunLevel Highest

ここでの重要ポイント:

  • -WindowStyle Hidden: 黒い画面が一瞬出るのを防ぎます。
  • -RunLevel Highest: 管理者権限が必要な操作(ドライブマウントなど)も通るようになります。
  • -ExecutionPolicy Bypass: スクリプト実行ポリシーのエラーを無視して強制実行させます。

技術的なハマりどころ(FAQ)

Q. PowerShell のバージョン違い (powershell vs pwsh)

Windows には「Windows PowerShell (5.1)」と「PowerShell Core (pwsh)」の2つが入っていることが多いです。
タスクスケジューラで単に powershell と書くと、古い 5.1 が呼ばれます。
5.1 は挙動がレガシーですが、標準搭載されているため「5.1 でも動く書き方」をしておくのが最も移植性が高いです。

Q. WSL の Python を直接叩いてはダメ?

wsl python3 script.py のように Windows から WSL の Python を叩くことは可能です。
しかし、これだとGUIアプリや通知、クリップボード連携などで Windows 側の機能を使いたい場合にハマります。
「Windows の操作を自動化したい」なら Windows の Python を、「Linux の計算処理をさせたい」なら WSL の Python を、と明確に使い分けましょう。


まとめ:この構成がもたらすメリット

このアーキテクチャ(ドライブマウント + ブートストラップ + タスクスケジューラ)を採用すると、世界が変わります。

  1. パスエラーが消滅する
    • UNC paths are not supported という呪いの言葉を見なくて済みます。
  2. ポータビリティが上がる
    • スクリプト内でドライブを定義するため、どの PC に持っていっても動きます。
  3. デバッグが容易
    • run_task.ps1 をダブルクリックするだけで挙動確認ができるようになります。

Windows と WSL の連携は、「見えない壁(UNCパスと権限)」をどう迂回するか が全てです。
正面突破しようとせず、この「作法」に則ることで、驚くほど安定した自動化環境が手に入ります。

Discussion