param( [Parameter(Mandatory=$true)] [string]$Command, [string]$WorkDir = $null ) # 1. Resolve work directory. if ([string]::IsNullOrEmpty($WorkDir)) { $WorkDir = $PSScriptRoot } else { # Expand variables in WorkDir, such as $env:APPDATA. $WorkDir = $ExecutionContext.InvokeCommand.ExpandString($WorkDir) } # Make sure the directory exists. if (-not (Test-Path $WorkDir)) { Write-Warning "`n[CMD] WARNING: The work dir does not exist, using ($WorkDir)" $WorkDir = $PSScriptRoot } # 2. Expand variables in the command. $ProjectRoot = Split-Path $PSScriptRoot -Parent # Replace $PSScriptRoot with the project root path. # Direct string replacement is the safest option for backslashes. if ($Command.Contains('$PSScriptRoot')) { $Command = $Command.Replace('$PSScriptRoot', $ProjectRoot) } # This allows commands such as "echo $env:USERNAME". $Command = $ExecutionContext.InvokeCommand.ExpandString($Command) Write-Host "`n[CMD] Execute: $Command" -ForegroundColor Gray # 3. Start cmd.exe. # /c closes cmd after the command completes. # /s enables cmd quote handling for complex command strings. $processOptions = @{ FilePath = "cmd.exe" ArgumentList = "/s", "/c", "`"$Command`"" WorkingDirectory = $WorkDir Wait = $true NoNewWindow = $true PassThru = $true } try { $proc = Start-Process @processOptions # 4. Check exit code. if ($proc.ExitCode -ne 0) { Write-Error "`n[CMD] Failed to execute, exit code: $($proc.ExitCode)" } } catch { Write-Error "`n[CMD] Failed to start process: $_" }