PowerShellでsudoがしたかったんですよ.
背景
みむら親方が以前ブログに書いてたのを参考にしたのですが,
これだと第一引数を実行ファイルとして管理者権限で起動するだけなので,コマンドや引数付きでの実行ができなかったわけです.
そこで,以下のようにしました.
Pauseが定義されてることを前提とします.
これを,$profile
に書くなどすれば,起動時に読み込まれてsudo
が使えます.
本体
function Invoke-CommandRunAs
{
$cd = (Get-Location).Path
$commands = "Set-Location $cd; Write-Host `"[Administrator] $cd> $args`"; $args; Pause; exit"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($commands)
$encodedCommand = [Convert]::ToBase64String($bytes)
Start-Process powershell.exe -Verb RunAs -ArgumentList "-NoExit","-encodedCommand",$encodedCommand
}
Set-Alias sudo Invoke-CommandRunAs
function Start-RunAs
{
$cd = (Get-Location).Path
$commands = "Set-Location $cd; (Get-Host).UI.RawUI.WindowTitle += `" [Administrator]`""
$bytes = [System.Text.Encoding]::Unicode.GetBytes($commands)
$encodedCommand = [Convert]::ToBase64String($bytes)
Start-Process powershell.exe -Verb RunAs -ArgumentList "-NoExit","-encodedCommand",$encodedCommand
}
Set-Alias su Start-RunAs
解説
全体
Invoke-CommandRunAs
がメイン.
基本的には$commands
に実行するものを全部放り込んで,Base64でエンコードして管理者権限のpowershell.exeに渡します.
PowerShellコマンドは;で区切ってやれば次の行として扱われます.
この用法はpowershell /?
で確認できます.
$commandsの中身は以下のようになっています.
Set-Location $cd
親方も言っていたとおり,Start-Process -Verb RunAs
すれば管理者権限で起動ができるのですが,このときワーキングディレクトリが強制的にSystem32になり,-WorkingDirectory
オプションが効きません.
なので,コマンド実行前にSet-Location
をかけます.
Write-Host
"[Administrator] $cd> $args"
何が実行されたのか分かりづらかったのでワーキングディレクトリと実行内容を出しておきます.
$args
関数に渡ってきた引数をそのまま全部コマンドとみて実行します.
Pause; exit
実行後は開いた新しいPowerShellウインドウが勝手に閉じないようにPause
を置いていたのですが,それだけだとエラー時にPause
の行まで行かずに閉じてしまうので,powershell.exeに-NoExit
オプションをつけた上で,正常にPauseを通り過ぎたらexit
するようにしています.
おまけ
Start-RunAs
は現在のワーキングディレクトリで管理者のPowerShellウインドウを開くコマンドです.残念ながらインラインでやる方法は分かりませんでした.
これで,sudo Enable-PSRemoting
とかsudo cmd /c mklink a b
とかを管理者で走らせられるようになりました.もちろん実行前にはUACのダイアログが出ます.