How to run a PowerShell script within a Windows batch file
This one only passes the right lines to PowerShell:
dosps2.cmd
:
@findstr/v "^@f.*&" "%~f0"|powershell -&goto:eofWrite-Output "Hello World" Write-Output "Hello some@com & again"
The regular expression excludes the lines starting with @f
and including an &
and passes everything else to PowerShell.
C:\tmp>dosps2Hello WorldHello some@com & again
It sounds like you're looking for what is sometimes called a "polyglot script". For CMD -> PowerShell,
@@:: This prolog allows a PowerShell script to be embedded in a .CMD file.@@:: Any non-PowerShell content must be preceeded by "@@"@@setlocal@@set POWERSHELL_BAT_ARGS=%*@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([char]10,$((Get-Content '%~f0') -notmatch '^^@@'))) & goto :EOF
If you don't need to support quoted arguments, you can even make it a one-liner:
@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %*);'+[String]::Join([char]10,(Get-Content '%~f0') -notmatch '^^@PowerShell.*EOF$')) & goto :EOF
Taken from http://blogs.msdn.com/jaybaz_ms/archive/2007/04/26/powershell-polyglot.aspx. That was PowerShell v1; it may be simpler in v2, but I haven't looked.
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 offsetlocalset "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 & setlocalset "POWERSHELL_BAT_ARGS=%*"echo ---- FROM BATCHpowershell -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.