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のダイアログが出ます.