powershell 関係のアドベントカレンダーがこれぐらいしか無かったので取り合えずこちら投稿しました。
bat コマンドで高機能な処理を実装するのは非常に難しく、把握するにも昨今のスクリプト言語と比べると言語距離が離れており複雑怪奇な印象が拭えません。
私からのお願いです bat のコマンド類を忘れて下さいどうしても bat で実行する必要があるなら...
bat の中に powershell スクリプトを書きましょう!
ってな事で↓を参考にして下さいです。(因みに拡張子を ps1 にしても動作しますよ。
<# : UTF8CRLF -- batch containing a powershell script.
@setlocal & chcp 65001 >nul & call :__BATCH__ %* <nul
@exit /b %ERRORLEVEL%
:__BATCH__
@set "_=%~dpf0" & powershell -ExecutionPolicy ByPass -NoProfile -Command ^
"\".{$(Get-Content -Raw -Encoding UTF8 %~dpf0)}%*\"|Invoke-Expression"
@exit /b %ERRORLEVEL%
#>
Set-StrictMode -Version Latest # or 5.1
$ErrorActionPreference = 'Stop'
function Get-Prefix {'che⠀'}
function Get-Version {'0.0.0'}
function Get-HelpMessage
{
$cmd = $Params.MainCmd.BaseName
"usage: $cmd [subcmd|${cmd}opt...] subopt..."
"= subcmd ="
" @version@ .. output version."
" @help@ .. this messages."
" @init@ .. initialize configure file."
" @admin@ prg args .. run as admin."
""
"= ${cmd}opt ="
" - .. remaining opts for subopt."
" --conf=path .. path to '.${cmd}-conf.ps1'"
" --verbose .. verbosy messages."
" --help .. help on a specific subcmd. "
" [-examples|-detailed|-full]"
}
function Get-PSCommandPath {if ($PSCommandPath) {$PSCommandPath} else {$Env:_}}
function Get-PSScriptRoot {$cmd = Get-PSCommandPath ; [System.IO.Path]::GetDirectoryName($cmd)}
function Get-DefaultConfigure
{
"# UTF8CRLF -- configure for '$($Params.MainCmd)'"
}
#
#
#
function che⠀exit
{
<#
.SYNOPSIS
Test ExitCode.
#>
Param([Parameter()][int]$ExitCode = 0)
exit $ExitCode
}
function che⠀error
{
<#
.SYNOPSIS
Test Write-Error
#>
Param([Parameter(ValueFromRemainingArguments)][string[]]$Messages)
"""$Messages""" | Write-Error
exit 9999 # NOTREACHED
}
#
#
#
function che⠀version
{
<#
.SYNOPSIS
Output version of che⠀
#>
Get-Version
}
function che⠀help
{
<#
.SYNOPSIS
Type 'che⠀help' for usage.
Type 'che⠀subcmd --help [-examples|-detailed|-full]' for help on a specific subcmd.
'che⠀subcmd' is not a real cmdlet. Ignore the remark section.
#>
(Get-HelpMessage) -replace '@', '' | Write-Host
}
function che⠀init
{
<#
.SYNOPSIS
Initialize configure file
#>
$txt = Get-DefaultConfigure
$cnf = $Params.Configure
New-Item $cnf -Force -Type File -Value $txt | Out-Null
. $cnf
"Please edit '$cnf'." | Write-Host
}
function che⠀admin
{
<# use sudo
.SYNOPSIS
Invoke program as Administrator.
.EXAMPLE
che⠀admin cmd /c pause
#>
param(
[Parameter(Mandatory)][string]$Program,
[Parameter(ValueFromRemainingArguments)][string[]]$Arguments
)
$wdir = Get-Item .
$opts = @{
PassThru = $true
FilePath = 'powershell'
ArgumentList = "-c `"cd $($wdir.FullName) ; $Program $Arguments`""
}
$opts.ArgumentList | Write-Verbose
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")) {
$opts += @{Verb = 'RunAs'}
}
$proc = Start-Process @opts
$abug = if ($PSVersionTable.PSVersion.Major -eq 5) {$proc.Handle}
$abug = $abug
$proc.WaitForExit()
exit $proc.ExitCode
}
#
#
#
function Main
{
$Params = @{ArgList = $args}
$Params.MainCmd = Get-PSCommandPath | Get-Item
$Params.SubCmd = $subcmd = 'HELP'
$Params.Configure = "$PWD\.$($Params.MainCmd.BaseName)-conf.ps1"
$cmdopt = @()
$suball = @(
switch -regex (Get-HelpMessage) {"@(.+)@" {$Matches[1]}}
'exit'
'error'
)
$Params.SubOpt = $subopt = @($args | Foreach-Object {
if ($_ -like "-*") {
if ($cmdopt -contains '-') {$_}
elseif ($_ -eq '-') {$cmdopt += $_}
elseif ($_ -eq '--help') {$cmdopt += $_}
elseif ($_ -eq '--verbose') {$cmdopt += $_ ; $VerbosePreference = 'Continue'}
elseif ($_ -match '--conf=(.+)') {$cmdopt += $_ ; $Params.Configure = $Matches[1]}
else {$_}
}
elseif ($subcmd -ne 'HELP') {$_}
elseif ($suball -contains $_) {$Params.SubCmd = $subcmd = $_}
else {
"Unknown subcmd. '$_'" | Write-Error
exit 9999 # NOTREACHED
}
})
#
#
#
if ('--verbose' -in $cmdopt) {
$Params | Format-Table -Verbose
}
if ('--help' -in $cmdopt) {
"Get-Help che⠀$subcmd $subopt" | Write-Verbose
;Get-Help che⠀$subcmd @subopt
return
}
;"che⠀$subcmd $subopt" | Write-Verbose
. che⠀$subcmd @subopt
}
#
#
#
try {
Push-Location .
. Main @args
}
catch {
$sst = $_.ScriptStackTrace
$sst = $sst.Replace("<No file>", "$($Params.MainCmd.FullName)")
@(
"Fatal Error Occurred."
$_.Exception
$_.InvocationInfo.PositionMessage
"ScriptStackTrace:`n$sst"
) | Write-Host
exit 1
}
finally {
Pop-Location
}