PowerShell には su
コマンドおよび sudo
コマンドがありません。そのため、管理者としてコマンドを実行したい場合は powershell.exe
ないしは pwsh.exe
1 などのシェルを「管理者として実行」する必要があります。
だからといってすべてのコマンドを管理者として実行するのは危険ですよね。というわけで、Windows でも su とか sudo っぽいことがする方法を考えてみました。
TL;DR
こちらの Git Repositry から git clone
するなり Download ZIP
するなりしたファイル群を任意の場所に配置し、ルートディレクトリ(下図の場合は C:\Scripts
)にパスを通して下さい。
C:\Scripts\
├ su.bat
├ sudo.bat
└ sudo\
└ main.ps1
使い方:
PS> su
PS> sudo < コマンド | ファイル名 > [オプション...]
PowerShell で su
っぽいことがしたい
PowerShell で以下のコマンドを実行すると、管理者権限をもった PowerShell が新しいウィンドウで起動します:
PS> su
解説
@echo off
powershell -Command "Start-Process powershell -Verb runas"
PowerShell からバッチファイルを実行し、バッチファイルから PowerShell の Start-Process
コマンドを実行し、Start-Process
コマンドで管理者権限つきの PowerShell を立ち上げています。はちゃめちゃに迂遠ですが、これは ExecutionPolicy の制約で .ps1
ファイルが直接叩けない環境での利用を想定しているためです。コンプラに厳しい職場でも安心。
ちなみに Start-Process
コマンドには新たなウィンドウを立ち上げないオプションも存在しますが、-Verb runas
と排他になっています。無念。
PowerShell で sudo
っぽいことがしたい
PowerShell で以下のコマンドを実行すると、管理者権限をもった PowerShell が新しいウィンドウで起動し、指定したコマンドや .ps1
ファイルが実行されます:
PS> sudo < コマンド | ファイル名.ps1 > [オプション...]
なおファイル名やオプションに "
(ダブルクォーテーション)を入力したい場合は `
(バッククォート)でエスケープする必要があります( PowerShell は " "
の内側を文字列として解釈するため):
PS> sudo Start-VM `"CentOS 7`"
PS> sudo `"C:\path to scripts\script.ps1`" argument_1 `"argument 2`"
解説
@echo off
REM 「 」「,」「=」「;」で区切らず、引数を全部取得する
set arg=%*
REM 「"」を「\"」にエスケープする
set replacedArg=%arg:"=\"%
REM .\sudo\main.ps1にエスケープ済みの引数を渡す。
powershell -ExecutionPolicy Unrestricted -File "%~dp0sudo\main.ps1" "%replacedArg%"
ExecutionPolicy に怒られないように sudo.bat
を経由しています。バッチファイル内で "
を渡す際にはエスケープシーケンスとして \
をつけなければなりません。コマンドプロンプトと PowerShell と挙動が違うのがめんどくさいですね。
# 確認
Write-Host "sudo"$Args[0]
# ダブルクォーテーションで始まっているとき
if ( $Args[0].StartsWith("`"") ) {
# 一つ目のダブルクォーテーションと二つ目のダブルクォーテーションの間がファイル名
$s = $Args[0].Split("`"")
$filename = $s[1]
# 「"ファイル名"」直後に1文字以上あったら = 引数があったら
if ( $s[2].Length -gt 0) {
$arguments = $Args[0].Substring($filename.Length + 3, $Args[0].Length - $filename.Length - 3)
}
# 絶対パスに展開(管理者シェルは C:\WINDOWS\system32で開かれるため)
$path = "`"" + (Resolve-Path $filename).Path + "`""
Write-Host "ファイル名`t$filename"
Write-Host "フルパス`t$path"
Write-Host "オプション`t$arguments "
# コマンドラインオプションを生成
$argument_list = "-ExecutionPolicy Unrestricted -File " + $path + " " + $arguments
}
# ダブルクォーテーションで始まってないとき
else {
# スペースより手前がファイル名かコマンド
$s = $Args[0].Split(" ")
$filename = $s[0]
# スペースで区切ったとき2つ以上に分割されたら = 引数があったら
if ( $s.Length -gt 1) {
$arguments = $Args[0].Substring($filename.Length + 1, $Args[0].Length - $filename.Length - 1)
} else {
$arguments = ""
}
# ファイル名なら
if ( Test-Path $filename ) {
# 絶対パスに展開(管理者シェルは C:\WINDOWS\system32で開かれるため)
$path = "`"" + (Resolve-Path $filename).Path + "`""
Write-Host "ファイル名`t$filename"
Write-Host "フルパス`t$path"
Write-Host "オプション`t$arguments "
# コマンドラインオプションを生成
$argument_list = "-ExecutionPolicy Unrestricted -File " + $path + " " + $arguments
}
# コマンドなら
else {
Write-Host "コマンド`t$filename"
Write-Host "オプション`t$arguments "
# コマンドラインオプションを生成
#「"」 を 「\」 でエスケープ
$argument_list = "-Command " + $filename + " " + $arguments.replace("`"", "\`"")
}
}
# 管理者シェルを起動する
Start-Process powershell -ArgumentList $argument_list -Verb runas -Wait
Start-Process powershell -Command <コマンド> [オプション]
を呼び出す際に "
を `\
でエスケープしています。PowerShell のエスケープシーケンスは `
ですが、 -Command
オプションではコマンドプロンプトと同様のエスケープ処理が走るため \
も追加する必要があるようです。2