Whenever I use [Console]::Error.WriteLine
to force write output to STDErr
when embedded in a PowerShell EncodedCommand
that’s called from inside a Batch Script For Loop
, the STDErr
text all appears to run as though it were “Front Loaded”, whereas ALL of the remaining functions/processes appear to run AFTER all STDErr
Output is long completed visually!
I KNOW this to be the case, because after all the output is displayed, the PowerShell EncodedCommand
is left in what appears to be a hanging state, but when I Tail
the log file, I can clearly see the file executing code which should have already been executed when the visible Output is displayed! (I also tested sending STDErr to the same log file and indeed – ALL of the STDErr verbiage is processed before the actual processes are finalized.) Is this standard behavior for intercepting PowerShell
command output using Batch Script For Loops
? What am I missing?
Technical Data / Code Examples etc.
Consider this PowerShell Script
below. It is converted to an EndodedCommand
– note – I left this conversion fluff out of the example.
Function Write_Log { Param([string]$textOut, [Parameter(Mandatory = $false)][boolean]$Out )if ($Out) {write-Host $textOut}; $myDate=(Get-Date -Format "yyyyMMddHHmmssff");$repl="`r`n" + $myDate + ' :: '; $textOut=$textOut -replace 'rn', $repl ; write-Output "$myDate :: $textOut"| Out-File -FilePath ("$Env:log" -replace '"','') -Append -Encoding default }
#This Function - Write_StdErr is what I am seeing bizarre effects from. No Errors.
Function Write_StdErr { Param([string]$textOut) If ([int]$Env:isGiveStatus) {Clear-Host; [Console]::Error.WriteLine($textOut); Start-Sleep -Milliseconds 250 } }
Function GetXMLValue {
#. . . Do Stuff for XML File to get values
# Note - I do not call Write_StdErr from this function at all, only Write_Log which sends text to STDOut where the BatchScript parses for variables later.
}
#Pre-populate vars for checking later since they are passed by reference
$ChkVal="null"
$curStat="null"
$curDenom="null"
$curCap="null"
$isRecycle="null"
$_XPath="$Env:xml_XPath"
For ($i=1; $i -le 8; $i++) {
#This Line will not be parsed by Batch Script:
Write_StdErr "Checking for Object [$i] Values"
$_File="$Env:xml_File"
$j=$i - 1
#Iterates through list of known XML Objects
If ($_XPath -clike '*_NUM_*') { $_XPath = $_XPath -replace '_NUM_', "$i" } else { $_XPath = $_XPath -replace "NO='0$j'","NO='0$i'"}
#This Line is parsed by Batch Script:
Write_Log "Current Object being checked: [$i]" $true
GetXMLValue "$_File" "Name" "$_XPath" ([ref]$curStat) 0 0
GetXMLValue "$_File" "TYPE" "$_XPath" ([ref]$isRecycle) 0 0
if ($curStat -cnotlike 'x*' -and $curStat -ne $null -and $isRecycle -eq 'RECYCLE') {
#This Line will not be parsed by Batch Script:
Write_StdErr "Current Object [$i] data found."
GetXMLValue "$_File" "SOME_OTHER_ATTRIBUTE" "$_XPath" ([ref]$curCap) 0 0
GetXMLValue "$_File" "ANOTHER_ATTRIBUTE" "$_XPath/CODE" ([ref]$curDenom) 0 0
} elseif ($isRecycle -eq 'AUDIT') {
#This Line is parsed by Batch Script:
Write_Log "Object #$i# is an AUDIT Object`r`n" $true
#This Line will not be parsed by Batch Script:
Write_StdErr "Current Object [$i] is an AUDIT Object"
} elseif ($curStat -eq $null) {
#This Line is parsed by Batch Script:
Write_Log "Object #$i# is missing from Config.`r`n" $true
#This Line will not be parsed by Batch Script:
Write_StdErr "Current Object [$i] does not exist in Config."
} else {
#This Line is parsed by Batch Script:
Write_Log "Current Object #$i# is not in use.`r`n" $true
#This Line will not be parsed by Batch Script:
Write_StdErr "Current Object [$i] is not in use."
}
}
#This Line will not be parsed by Batch Script:
Write_StdErr "Finished Checking File.`r`n"
Exit 0
}
The Script itself completes beautifully when ran through a For Loop
in a Batch Script
(again – as an EncodedCommand
– note – I’ve left the EncodedCommand
itself out, and I am well aware I need to escape the =
signs – again – no Errors) – see below.
-
My output when running in the
For Loop
is ONLY:Checking for Object [1] Values Current Object [1] is an AUDIT Object Checking for Object [2] Values Current Object [2] Data Found ... Checking for Object [8] Values Current Object [8] Data Found
Once the final iteration of the loop in PowerShell
completes – the log file appended by Write_Log
within the PowerShell EncodedCommand
is still receiving updates as the system processes appears to finally process the rest.
set "isGiveStatus=1" & REM This is to ensure the "Write_StdErr" Function sends Output.
REM This For Loop pulls data into various variables from the PowerShell's STDOut Output using this for Loop.
REM The Write_StdErr function in PowerShell is used to bypass the For Loop's processes and display the text raw to the user.
for /f "Tokens=2 Delims=[]" %%A in ('Powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand !newEncodeCMD!') do (
cls
ping 127.0.0.1 -n 1 >nul
ping 127.0.0.1 -n 1 >nul
ping 127.0.0.1 -n 1 >nul
If "%~7"=="CustomFunc" (
If "%%~A"=="" (
Call :WriteOut "The parameter from the PowerShell Script was empty." %log% "Yes"
) else (
REM Sub-Routine of Batch script provided as 8th argument like ":SubRoutineName"
Call %~8 "%%~A"
REM Note - Routine called has no values written to the Console - only data is logged to Log file.
set "Powershell_Rslt=!errorlevel!"
)
) else (
set "Powershell_Rslt=!errorlevel!"
REM Echo %%A
set "%~8=%%A"
)
)