WSL のシェルから Windows のバッチファイルを管理者権限で起動するシェルスクリプトの作成で得られたバッドノウハウ

タイトルが長いのはご容赦ください。ここでは、WSL で利用するシェルスクリプトを作成した時に得られたバッドノウハウを 3 点紹介します。 このシェルスクリプトを作成する時は知らなかったのですが、[Sudo for WSL (wsl-sudo)](https://github.com/Chronial/wsl-sudo)[^whatisthis] というものがあります。試してみたことはありません。バッドノウハウよりも今使えるものが欲しいという方はこちらを試してみてはどうでしょうか?

# 作成したシェルスクリプトの概要
* WSL  のコマンドシェル or シェルスクリプトから起動することを想定
* 引数で指定されたバッチファイルを管理者権限で起動します
* バッチファイルは Windows の %TEMP% ディレクトリに存在するものとします

# 作成したシェルスクリプト[^dialogue]
```shell
     1	#!/bin/sh
     2	runfile="$1"
     3	cd /mnt/c
     4	
     5	u_psexe="/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe"
     6	m_psexe=$(wslpath -a -m "$u_psexe")
     7	
     8	u_cmdexe="/mnt/c/Windows/System32/cmd.exe"
     9	m_cmdexe=$(wslpath -a -m "$u_cmdexe")
    10	
    11	# Get value of %TEMP% with long file name
    12	w_temp=$($u_psexe 'Write-Host (Get-Item -LiteralPath $Env:TEMP).FullName' < /dev/null | sed -e 's/\r$//')
    13	u_temp=$(wslpath -a -u "$w_temp")
    14	m_temp=$(wslpath -a -m "$u_temp")
    15	
    16	# Execute $runfile as admin
    17	cd "$u_temp"
    18	case "$runfile" in
    19	*.bat)
    20	    $u_psexe -NoProfile Start-Process -Wait -Verb RunAs -WorkingDirectory "$m_temp" -FilePath "$m_cmdexe" -ArgumentList "'/C $m_temp/$runfile'" < /dev/null
    21	    ;;
    22	*.ps1)
    23	    $u_psexe -NoProfile Start-Process -Wait -Verb RunAs -WorkingDirectory "$m_temp" -FilePath "$m_psexe" -ArgumentList "'-NoProfile -ExecutionPolicy RemoteSigned -File $m_temp/$runfile'" < /dev/null
    24	    ;;
    25	esac
```

# コマンド説明
### `wslpath`
バッドノウハウの前に、6, 9, 13, 14 行目で使われている `wslpath` コマンドについて簡単に説明します。WSL でディストリビューションをインストールすると自動的に作成されるようです。実態は以下のように `/init` へのシンボリックリンクです。
```console
$ ls -l $(which -a wslpath)
lrwxrwxrwx 1 root root 5 Nov 19 15:28 /bin/wslpath -> /init
lrwxrwxrwx 1 root root 5 Nov 19 15:28 /usr/bin/wslpath -> /init
```
`wslpath` を引数なしで起動すると使用例が表示されます。Windows のパス表記を UNIX 系の表記に変換したり、その逆をしたりする機能があります。下表に示すようなパス表記の変換ができます。
```console
$ wslpath
wslpath: Invalid argument
Usage:
    -a    force result to absolute path format
    -u    translate from a Windows path to a WSL path (default)
    -w    translate from a WSL path to a Windows path
    -m    translate from a WSL path to a Windows path, with '/' instead of '\'

EX: wslpath 'c:\users'
```

| オプション指定 | 出力例                    | 
|:------------:|:-------------------------|
| -u           | /mnt/c/Windows/System32  |
| -w           | C:\Windows\System32      |
| -m           | C:/Windows/System32      |
	
	

今回作成したシェルスクリプトでは、パス名を含むシェル変数にどの形式のパス名が設定されているかを示すため、`u_`, `w_`, `m_` のプリフィクスをつけてあります。


# バッドノウハウ

### (その 1) &nbsp; 3 行目
`cd /mnt/c`
cmd.exe などは WSL 側のパスを理解できないようです。WSL から cmd.exe を起動すると、以下のようなメッセージが表示されます。一旦 、C ドライブ直下などに避難して回避します。
```
'\\wsl.localhost\Ubuntu-24.04\home\username’
上記の現在のディレクトリで CMD.EXE を開始しました。
UNC パスはサポートされません。Windows ディレクトリを既定で使用します。
```

&nbsp;
### (その 2) &nbsp;  11 行目
環境変数 TEMP のパス名が[ショートパス名](https://ja.wikipedia.org/wiki/8.3%E5%BD%A2%E5%BC%8F#%E7%9F%AD%E3%81%84%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D)になっている場合があります。ショートパス名は `wslpath` では扱えないようです、あらかじめここでロングパス名に展開します。

&nbsp;
### (その 3) &nbsp;  12, 20, 23 行目
Windows 側のコマンドを実行すると、なぜか標準入力が吸い取られます。この問題に[関連する記事がこちら](https://qiita.com/yabeenico/items/608a7bed756ffce9c9b6)にあります。`< /dev/null` によるリダイレクトはその対策です。この記事の説明がシンプル過ぎるので、わかりやすそうな例を示します。
```shell
cat file.txt | while read line; do
    …
    echo line=$line
    なんとか.exe
    …
done
```
このようなシェルスクリプトの場合、file.txt に複数行あったとしても、`なんとか.exe` が起動する際に、標準入力が全部吸い込まれてしまうため、ループは 1 回で終了してしまいます。`line=...` は file.txt の 1 行目だけが表示されます。

今回作成したシェルスクリプトをコマンド行から起動しているだけならば気がつかないかもしれません。このシェルスクリプトを別のシェルスクリプトに組み込んだ場合などで、実行時に標準入力が読み込み可能な状態だと、上記の問題が発生します。

[^whatisthis]: [Sudo for WSL (wsl-sudo)](https://github.com/Chronial/wsl-sudo) / 以下「What is this?」より抜粋
_This tool allows you to run applications in windows elevated user mode from a non-elevated wsl shell. It has full terminal support, so you can run interactive applications like vim or a shell through it._

[^dialogue]: [UAC](https://learn.microsoft.com/ja-jp/windows/security/application-security/application-control/user-account-control/
) による権限の昇格が発生すると、ダイアログ・ウインドウが表示されます。これは手動で入力、もしくは、クリックして通過してください。スクリプトにより自動的に指定されたバッチファイルを起動しますが、無人で利用可能というわけではありません。