Powershell で作ったツールを簡単に配布してダブルクリックで起動したい
昔(20年くらい?)から私はWSH(VBScript)をよく使っていたのですが、最近の若い者から「今はPowershellですよ。VBScriptなんて、もうすぐ無くなりますよ」と年寄り扱いされたので、去年からPowerShellを使うようになりました。
使ってみるとpowershell は強力なんだけど ps1 ファイルを配布しただけでは、ダブルクリックしても起動しないので、ツールとしては不便を感じていました。
いろいろやってみて WSH(VBScript) みたいにツール単体を配布するだけで簡単に起動できるものが出来たので、投稿します。
.bat にすることで起動方法のバリエーションが増えるので、ツールとしての利便性が格段に上がります。
是非お試しください。
私は powershell で作ったツールは全部この方法で起動してます。一度使ったら止められません。
==> 以前は Win10 で Windows PowerShell Integrated Scripting Environment (ISE)を使って編集していましたが、Win11 になってからは Visual Studio Code を使っているので、それを前提にしています。
作り方
- Win11以降のPowershell は Pwsh(7.x utf8ベース)が標準でインストールされているので、そちらを前提にしています。したがって、ファイルは utf8 で保存してください
- powershell を普通に(Visual Studio Code などで).ps1 で作ります。
- パラメータで渡したいものは、変数 $parm[0..8] にセットされている前提で作ってください。
- 以下の HelloWorld.bat の最後の1行(Write-Host の行)以外の部分を .ps1 の頭の部分に挿入して一旦保存して エディターを閉じます
- ファイル名を .bat に変更します
- ファイルをメモ帳 or Visual Studio Code などで開いてみて ANSI (Shift_JIS) だった場合は、UTF-8 (BOM なし) で保存しなおします。
:DUMMY for($i=1;$i -eq 0;$i++) {echo DUMMY} <#
chcp 65001
@rem "上の行は batでは(goto されない)ラベル。パワーシェルでは(条件は常にfalseで実行されない)ラベル付きfor"
@rem "bat でも パワーシェルでもエラーにならずに、パワーシェルにとってのコメントブロックを開始したかっただけ。"
@rem "2行目以降は bat を実行するエリア。パワーシェルから見れば単なるコメントブロック。"
@rem "/C オプションのときは待たずに終了。オプションがなければ PAUSE で待つ。"
@rem "bat に渡された引数は、パワーシェルに$parm[0..8]で渡す。"
@echo off
set ME=%~dpnx0
rem "/C オプションの有無をチェック"
if /i CHK%1==CHK/C (
set CHK=EXIT
shift
) else (
set CHK=PAUSE
)
rem "パワーシェルを実行。自分自身をコマンドの入ったテキストとして取込んで実行。"
pwsh -ExecutionPolicy RemoteSigned -Command "Invoke-Expression -Command (@('$parm=@(\"%1\",\"%2\",\"%3\",\"%4\",\"%5\",\"%6\",\"%7\",\"%8\",\"%9\")') + (Get-Content '%ME%') -join \"`n\")"
if /i %CHK%==EXIT exit /b
pause
exit /b
bat のエリアは、exitまで。ここから先は(exit後なので)bat にとっては単なるゴミ #>
# ここから下はパワーシェルの領域
Write-Host "Hello World!"
.bat の中にコメント(rem 文)を入れてあります。何をやっているのかは、そちらをご覧ください。
最大のポイントは1行目。powershell のブロックコメント <# を始めたかったのですが、不用意に書くと bat でリダイレクトとして認識されてしまうので、これを避けるために工夫してます。
これ以外の書き方もあると思います。誰か、もっとカッコいい方法があったら教えてください。
パラメータ渡しのサンプル tail.bat
(長いログファイルの最後の部分だけ見たかったので作りました)
powershell へのパラメータの渡し方のサンプルとして、tailコマンドの例を以下に添付します。
サンプルでは input のファイルをパラメータとして渡してます。
tai.bat の使い方は以下の通り、いろいろな起動方法で実行できるので、ps1 より格段に便利です。
- tail.bat をダブルクリックして実行して、コマンドラインからファイル名を手で入力する
- 入力のtxtファイルをマウスでドラッグして、tail.bat の上にドロップする
- コマンドラインからコマンド入力=> [パス]tail [/C] [/N 行数] [ファイル名]
- バッチファイルの中から呼ぶ=> CALL [パス]tail.bat /C [/N 行数] ファイル名
:DUMMY for($i=1;$i -eq 0;$i++) {echo DUMMY} <#
chcp 65001
@rem "上の行は batでは(goto されない)ラベル。パワーシェルでは(条件は常にfalseで実行されない)ラベル付きfor"
@rem "bat でも パワーシェルでもエラーにならずに、パワーシェルにとってのコメントブロックを開始したかっただけ。"
@rem "2行目以降は bat を実行するエリア。パワーシェルから見れば単なるコメントブロック。"
@rem "/C オプションのときは待たずに終了。オプションがなければ PAUSE で待つ。"
@rem "bat に渡された引数は、パワーシェルに$parm[0..8]で渡す。"
@echo off
set ME=%~dpnx0
rem "/C オプションの有無をチェック"
if /i CHK%1==CHK/C (
set CHK=EXIT
shift
) else (
set CHK=PAUSE
)
rem "パワーシェルを実行。自分自身をコマンドの入ったテキストとして取込んで実行。"
pwsh -ExecutionPolicy RemoteSigned -Command "Invoke-Expression -Command (@('$parm=@(\"%1\",\"%2\",\"%3\",\"%4\",\"%5\",\"%6\",\"%7\",\"%8\",\"%9\")') + (Get-Content '%ME%') -join \"`n\")"
if /i %CHK%==EXIT exit /b
pause
exit /b
bat のエリアは、exitまで。ここから先は(exit後なので)bat にとっては単なるゴミ #>
# ここから下はパワーシェルの領域
# Write-Host "受け取ったパラメータ" ($parm[0..8] -join ", ")
# パラメータから /N スイッチをチェックして行数をセット。デフォルトは10行
$idx=0
if ($parm[$idx] -eq "/N") {
$idx++
$num=$parm[$idx]
$idx++
} else {
$num=10
}
# ファイル名を取得
if ($parm[$idx] -eq "") {
$mymsg = "`r`ntail [/C] [/N 行数] [ファイル名] : ファイルの最後のn行(デフォルト10行)を出力。/C でpauseせずに画面を閉じます`r`n`r`n"
$current=Get-Location
$mymsg = "$mymsg `r`nカレントフォルダ $current `r`n`r`n対象ファイルの PATH を入力してください"
$input = Read-Host $mymsg
} else {
$input = $parm[$idx]
}
if ($input -eq ""){
Write-Host "`r`nファイル名が空です`r`n`r`n"
exit
}
if (Test-Path $input) {
$fullpath = $input
} else {
Write-Host "`r`n$input が見つかりません`r`n`r`n"
exit
}
# ファイルの最後のn行を取得
Get-Content -Path $fullpath -Tail $num
##あとがき
昔これと同じ発想で perl を起動していました。当時は perl 結構流行ってたんですよね。最近あまり見かけませんね。
Power shell は何でもできるのは運用周りのスクリプトとして優れてますが、やっぱりとっつきにくいですよね。コマンドが独特で。
こちらのwebsiteもよろしくお願いします。
[初心者のためのWEBシステムのインフラ構築]
##以前のバージョン
以前の投稿に掲載していた、Win10 以前に標準搭載されていた古いPowerShell(バージョン5.1まで ShiftJIS ベース)用のスクリプトはこちらです
- powershell を普通に(Windows Powershell ISE などで).ps1 で作ります。
- パラメータで渡したいものは、変数 $parm[0..8] にセットされている前提で作ってください。
- 以下の HelloWorld.bat の最後の1行(Write-Host の行)以外の部分を .ps1 の頭の部分に挿入します
- ファイルをメモ帳で開いてみて UTF-8 だった場合は、ANSI (Shift_JIS) で保存しなおします。
- ファイル名を .bat に変更します
なお、ファイル名を .bat に変更するよりも .ps1 で編集して出来上がったら .bat をハードリンクで作成しておくと、実行時は .bat 編集時は .ps1 としてエディタ(ISE)で編集できるので便利です。
- 実行用の.bat をハードリンクで作ります。=> DOS 窓を管理者権限で開いて、mklink /H xxxxxx.bat xxxxxx.ps1
:DUMMY for($i=1;$i -eq 0;$i++) {echo DUMMY} <#
@rem 上の行は batでは(goto されない)ラベル。
@rem パワーシェルでは(条件は常にfalseで実行されない)ラベル付きfor
@rem bat でも パワーシェルでもエラーにならずに、パワーシェルにとってのコメントブロックを開始。
@rem 2行目以降は bat を実行するエリア。パワーシェルから見れば単なるコメントブロック。
@rem /C オプションのときは bat は待たずに終了。オプションがなければ PAUSE で待つ。
@rem bat に渡された引数は、パワーシェルに$parm[0..8]で渡す。
@echo off
set ME=%~dpnx0
rem /C オプションの有無をチェック
if /i CHK%1==CHK/C (
set CHK=EXIT
shift
) else (
set CHK=PAUSE
)
rem パワーシェルを実行。自分自身をコマンドの入ったテキストとして取込んで実行。
powershell -ExecutionPolicy RemoteSigned -Command "Invoke-Expression -Command (@('$parm=@(\"%1\",\"%2\",\"%3\",\"%4\",\"%5\",\"%6\",\"%7\",\"%8\",\"%9\")') + (Get-Content '%ME%') -join \"`n\")"
if /i %CHK%==EXIT exit /b
pause
exit /b
bat のエリアは、exitまで。ここから先は(exit後なので)bat にとっては単なるゴミ #>
# ここから下はパワーシェルの領域
Write-Host "Hello World!"