How do I have a PowerShell script embedded within the same file as a Windows batch script?
I know this kind of thing is possible in other scenarios:
Here the topic has been discussed. The main goals were to avoid the usage of temporary files to reduce the slow I/O operations and to run the script without redundant output.
And here's the best solution according to me:
<# :
@echo off
setlocal
set "POWERSHELL_BAT_ARGS=%*"
if defined POWERSHELL_BAT_ARGS set "POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%"
endlocal & powershell -NoLogo -NoProfile -Command "$input | &{ [ScriptBlock]::Create( ( Get-Content \"%~f0\" ) -join [char]10 ).Invoke( @( &{ $args } %POWERSHELL_BAT_ARGS% ) ) }"
goto :EOF
#>
param(
[string]$str
);
$VAR = "Hello, world!";
function F1() {
$str;
$script:VAR;
}
F1;
An even better way (seen here):
<# : batch portion (begins PowerShell multi-line comment block)
@echo off & setlocal
set "POWERSHELL_BAT_ARGS=%*"
echo ---- FROM BATCH
powershell -noprofile -NoLogo "iex (${%~f0} | out-string)"
exit /b %errorlevel%
: end batch / begin PowerShell chimera #>
$VAR = "---- FROM POWERSHELL";
$VAR;
$POWERSHELL_BAT_ARGS=$env:POWERSHELL_BAT_ARGS
$POWERSHELL_BAT_ARGS
where POWERSHELL_BAT_ARGS
are command line arguments first set as variable in the batch part.
The trick is in the batch redirection priority - this line <# :
will be parsed like :<#
, because redirection is with higher priority than the other commands.
But the lines starting with :
in batch files are taken as labels - i.e., not executed. Still this remains a valid PowerShell comment.
The only thing left is to find a proper way for PowerShell to read and execute %~f0
which is the full path to the script executed by cmd.exe.