0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

bat の中で powershell を書きましょう!

Last updated at Posted at 2025-12-05

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 cheexit
{
    <#
        .SYNOPSIS
            Test ExitCode.
    #>
    Param([Parameter()][int]$ExitCode = 0)
    exit $ExitCode
}

function cheerror
{
    <#
        .SYNOPSIS
            Test Write-Error
    #>
    Param([Parameter(ValueFromRemainingArguments)][string[]]$Messages)
    """$Messages""" | Write-Error
    exit 9999 # NOTREACHED
}

#
#
#

function cheversion
{
    <#
        .SYNOPSIS
            Output version of che⠀
    #>
    Get-Version
}

function chehelp
{
    <#
        .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 cheinit
{
    <#
        .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 cheadmin
{
    <# 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
}
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?