It is possible to execute the PowerShell code in a batch file, but technically what you are doing is pulling a copy of it out and executing it someplace else. Here are 3 methods that I know of.
- mklement0's answer addresses executing a copy of it that is passed as a parameter to PowerShell.
- You could build a ps1 file from CMD, and then execute that ps1 file by passing it as a parameter to PowerShell.
- And the method I've worked with the most is to pass specially designed PowerShell code to PowerShell that, when it runs, will load all, or part, of the current CMD file into memory and execute it there as a ScriptBlock. I have tried loading parts of the current CMD file, but my experience has been that this gets too complicated and I just stick with loading the entire current CMD file.
That last method is what I'm presenting here. The trick is to make the batch/CMD portion of the script look like a comment that is ignored by PowerShell, but still runs without throwing error messages in CMD. I'm not sure where I first found this trick, but it goes like this:
- First, place
<# :
at the start of script. PowerShell sees this as the start of a comment, but CMD seems to ignore this line. I think CMD is trying to redirect <
the contents of a non-existing file :
to a non-existing command. But what does CMD do with the #
? It works, and that's the important thing.
- Place your batch code in lines following the
<# :
.
- You end the batch/CMD part with a
GOTO :EOF
.
- You then end the PowerShell comment with
#>
, but visually I find it easier to find <#~#>
, which does the same job.
- The rest of the file is your PowerShell code.
This version treats the PowerShell code as a function with defined parameters. The batch part builds %ARGS%
and passes, with double quotes intact, to a PowerShell ScriptBlock that in turn is wrapped in another ScriptBlock. The PowerShell function is called twice with the same SourceFile parameter, but different DestinationFile and TextToRemove parameters. Perhaps there is a simpler way to reliably pass double quotes "
in arguments passed to a ScriptBlock from batch, but this is the method I got working.
<# :
@ECHO OFF
SET f0=%~f0
SET SourceFile=%APPDATA%\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt
SET ARGS="%SourceFile%" "%APPDATA%\BIOS-Benchmark\Count_only.txt" "Count :"
PowerShell -NoProfile -Command ".([scriptblock]::Create('.([scriptblock]::Create((get-content -raw $Env:f0))) ' + $Env:ARGS))"
SET ARGS="%SourceFile%" "%APPDATA%\BIOS-Benchmark\Durchschnitt_only.txt" "Average :"
PowerShell -NoProfile -Command ".([scriptblock]::Create('.([scriptblock]::Create((get-content -raw $Env:f0))) ' + $Env:ARGS))"
GOTO :EOF
<#~#>
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$SourceFile,
[Parameter(Mandatory = $true, Position = 1)]
[string]$DestinationFile,
[Parameter(Mandatory = $true, Position = 2)]
[string]$TextToRemove
)
(Get-Content $SourceFile).Replace($TextToRemove, '') > $DestinationFile
This script passes a single parameter that, in PowerShell, is used by the Switch command to decide which section of PowerShell you intend on executing. Since we are not passing double quotes "
in the args, the PowerShell lines can be greatly simplified. Information could still be passed to PowerShell by defining environmental variables in batch and reading them in PowerShell.
<# :
@ECHO OFF
SET f0=%~f0
PowerShell -NoProfile -Command .([scriptblock]::Create((get-content -raw $Env:f0))) Script1
PowerShell -NoProfile -Command .([scriptblock]::Create((get-content -raw $Env:f0))) Script2
GOTO :EOF
<#~#>
switch ($args[0]) {
'Script1' {
(Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt).Replace("Count :", '') > $env:APPDATA\BIOS-Benchmark\Count_only.txt
break
}
'Script2' {
(Get-Content $env:APPDATA\BIOS-Benchmark\PowerShell-Protokoll-Auswertung.txt).Replace("Average :", '') > $env:APPDATA\BIOS-Benchmark\Durchschnitt_only.txt
break
}
default {}
}