紡Book の勉強ノートに戻る
開発記録2026.07.16

渡したはずのパスが、消えて届く・化けて届く — Windows と WSL の境界で踏んだ2つの事故

WSLWindows開発環境紡Book

Windows と WSL をまたいで開発しているとき、同じ日に2回連続で「渡したコマンドが化ける」ところに突き当たった記録です。

変数に入れたパスが空文字になって届く

Windows 側から WSL の中へファイルをコピーしようとして、こういう形のコマンドを投げた。

bash
# wsl -d Ubuntu --  = 「Ubuntu(WSL)側で、この後ろを実行して」の合図
# bash -c '...'     = クォートの中身をまるごと「引数」として WSL に手渡す
#                     ↓ この $SRC が、あとで問題になる
wsl -d Ubuntu -- bash -c 'SRC="/mnt/c/.../テンプレ推し8_洗練案.html"; cp "$SRC" ...'

返ってきたエラーは cp: cannot stat '': No such file or directory。stat は「そのファイルの情報を見る」の意味で、その対象が ''(=空っぽ)になっている。存在しないファイルを探したのではなく、ファイル名そのものが消えて届いていた。

最初は日本語ファイル名のせいだと思って ASCII だけのファイル名で試したのだが、同じように空文字で落ちた。犯人は日本語ではなく $SRC のほうだった。SRC=hello のような単純な代入でも空になります。

文字化けなら cannot stat 'テン...' のように壊れた文字が出るはずで、空文字は「変数が消えた」ときの症状です。エラーの見た目から原因を取り違えていた。

この一行が bash に届くまでには、Windows 側のシェル → wsl.exe → bash と、それぞれ別の流儀で引数を解釈する層が最低3枚挟まっています。

引数の文字列はプロセスのメモリにあり、保存しない。どれが先に $SRC を読んだかは未特定。Windows 側のシェルGit Bash または PowerShell引数はメモリ保存しないwsl.exeWindows のネイティブ引数を Linux へ渡す保存しないWSL の bash届いた $SRC は空cp: cannot stat ''ファイルは探していない交差は誰が $SRC を先に展開するか。置き場所ではない。層のどれかは未特定。

/mnt/c の先頭に Git Bash のインストール先がくっつく

回避のためスクリプトファイルを作り、Git Bash からこう実行した。

bash
# 中身は書かず、スクリプトファイルの「置き場所」だけを引数で渡す形に変えた
# ところが今度は、この /mnt/c/... 自体が書き換えられた
wsl -d Ubuntu -- bash "/mnt/c/Users/.../copy_refine.sh"

今度のエラーは bash: C:/Program Files/Git/mnt/c/Users/...: No such file or directory。渡したはずの /mnt/c/... の先頭に、Git Bash 自身のインストール先がくっついている。

パスの書き換えは Git Bash のプロセスメモリ。ディスク上の copy_refine.sh は触らない。Git Bash見立て: Windows の exe へ渡す/ 始まりは翻訳が要る/mnt/c は自分のルート配下C:/Program Files/Git を前置WSL の Linuxwsl.exe の向こう側Unix 風パスしか受け取らない正しいパスは /mnt/c/Users/...変換してはいけない相手書いた /mnt/c/... が、メモリ上で C:/Program Files/Git/mnt/c/... に化ける。bash はその化けたパスを見る。ディスクのファイルは書き換えない。

なお Git Bash 側にはこの翻訳を止めるスイッチがあって、頭に付けるだけで通ります。

bash
MSYS_NO_PATHCONV=1 wsl -d Ubuntu -- bash "/mnt/c/.../copy_refine.sh"

試したら確かに通った。ただし毎回付け忘れる未来しか見えなかったので、自分は「PowerShell から実行する」側に寄せた。MSYS がどういう条件で変換をかけているのか、その全体像までは追えていない。「/ 始まりが怪しい」までは掴んだが、どこまでが変換対象かは分かっていないまま、動く形に寄せて先に進んだ。

エラーメッセージに「変換しました」とは書いてくれないので、症状から原因を逆算するしかありません。

回避策: 引数で渡さず、ファイルで渡す

最終的に安定したのはこの形だった。やりたい処理をシェルスクリプトファイルに書き、PowerShell 経由で wsl -d Ubuntu -- bash /mnt/c/.../script.sh と実行する。

スクリプトの中身はディスク上の .sh。引数はプロセスのメモリで、保存しない。引数プロセスのメモリ変換装置を必ず通る$SRC は消え得るGit Bash ならパスも誤訳ファイルの中身ディスク上の .sh変換装置を通らない$SRC を読むのは bash 自身閉じても残るファイルへのパスまだ引数(メモリ)Git Bash からだと誤訳PowerShell ならそのまま保存しないパス翻訳は Git Bash が持ち込む層。PowerShell に替えると消える。$SRC が消えるほうは、引数として渡す限りどちらのシェルでも起きる。ファイルにするだけでは足りない。起動元まで含めて1セット。
渡し方 引数内の $変数 /mnt/c パス 日本語
Git Bash から bash -c で 消えることがある 誤訳される 届いた
PowerShell から bash -c で 消えることがある そのまま届く 届いた
スクリプトファイルに書く(PowerShell から実行) 安全(bash が読む) 安全 届いた

日本語は今回試した範囲ではどの経路でも化けなかった(コードページを 932 に落としても届いた)。Windows は wsl.exe に引数を UTF-16 で渡すので、途中に文字コードを落とす変換が挟まらないらしいです。最初に日本語を疑ったのは完全に的外れだった。

境界を越える情報は「引数」ではなく「ファイル」に載せる、が今回の結論。ただし Git Bash から起動すれば2つめの事故はそのまま再発します(実際に再発した)。起動元を PowerShell にするところまでが1セット。

学んだこと

  • Windows×WSL の境界には自動変換が複数枚挟まっていて、どれも黙って動きます。「渡したものがそのまま届く」前提で書かないようにした
  • 化けたら引数を削って切り分けます。容疑者は $変数(消える)と / 始まりのパス(誤訳される)。日本語は今回シロだった
  • なお、この境界には別種の落とし穴もあります。Windows 側から WSL の中のファイルを書き換えたとき、「保存しました」と出ているのに中身が変わっていないことがあります。書いたら検証まで含めて1セット——仕組みの話は別の機会に

この記事は2026年7月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。