I switched from PowerShell 5.1 to the latest PowerShell 7.5.1 and noticed that the errors that my scripts produce are formatted differently. This runs on Windows Server 2019 Standard [Version 10.0.17763.7136].
I'm running my PowerShell scripts by having this line in the CMD script:
With PowerShell 5:
powershell -command "& 'c:\binaries\scripts\ServiceNow.ps1'" >> my.log
With PowerShell 7:
pwsh.exe -command "& 'c:\binaries\scripts\ServiceNow.ps1'" >> my.log
This is an example of an error that I see in the log file from PowerShell 5:
Export-Csv : Cannot validate argument on parameter 'Encoding'. The argument "utf8NoBOM" does not belong to the set
"Unicode,UTF7,UTF8,ASCII,UTF32,BigEndianUnicode,Default,OEM" specified by the ValidateSet attribute. Supply an
argument that is in the set and then try the command again.
At C:\binaries\scripts\ServiceNow.ps1:64 char:75
+ ... | Export-Csv -Path $strSitesFileNameMain -Encoding utf8NoBOM -NoType ...
+ ~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.ExportCsvCommand
And this is an example of error message that I get from PowerShell 7:
[31;1mMethodInvocationException: [0mC:\binaries\scripts\ServiceNow.ps1:141[0m
[31;1m[0m[36;1mLine |[0m
[31;1m[0m[36;1m[36;1m 141 | [0m [36;1m$webclient.UploadFile($uri, $item.FullName)[0m
[31;1m[0m[36;1m[36;1m[0m[36;1m[0m[36;1m | [31;1m ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[0m
[31;1m[0m[36;1m[36;1m[0m[36;1m[0m[36;1m[31;1m[31;1m[36;1m | [31;1mException calling "UploadFile" with "2" argument(s): "The remote name could not be resolved"[0m
That weird character before [31;
has ASCII code 0x1B (27). Plus all these extra [36;1m
noise. I suspect that these are the codes to format the message in different colours when it is shown in the console, but in my case this output is simply redirected into a plain text log file using >> my.log
in the CMD script.
As you can see messages from PS5 are much more readable than from PS7.
How do I return back to that old message style?
There are two independent aspects to consider:
In Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), the default error formatting, as reflected in and determined by the $ErrorView
preference variable, is 'NormalView'
, whereas in PowerShell (Core) 7, it is (the newly introduced) 'ConciseView'
.
'ConciseView'
is generally less noisy than NormalView
while typically still being descriptive enough, but if you prefer the latter, you'll need to execute $ErrorView = 'NormalView'
in PowerShell (Core) 7[1] - this aspect is not covered in the solution below.PowerShell (Core) 7 - unlike Windows PowerShell - uses Virtual Terminal (VT) aka ANSI escape sequences embedded in its error messages to achieve coloring - these are the "weird characters" you're seeing (if you were to print the file content to a console (terminal), you'd see the coloring).
$PSStyle.OutputRendering = 'PlainText'
in-session or by setting environment variable NO_COLOR
to a value such as 1
before invoking PowerShell - see the about_ANSI_Terminals help topic.Thus, to suppress the use of VT sequences from cmd.exe
/ a batch file:
set NO_COLOR=1
pwsh.exe -File c:\binaries\scripts\ServiceNow.ps1 >> my.log
Note:
-File
rather than -Command
is used above, which obviates the need to use &
, the call operator, as well as any embedded quoting. For guidance on when to use -File
vs. -Command
, see this answer.
$PSStyle.OutputRendering
defaults to 'Host'
, which indicates that VT sequences are only to be used when printing output to the host (console), as apposed to output being captured in a variable, sent through the pipeline to another command, or redirected to a file.
While this logic applies in-session, it inexplicably does not apply to calls to pwsh
, the PowerShell (Core) 7 CLI: the fact that the CLI output is being redirected to a file (>> my.log
) is not detected - hence the need for set NO_COLOR=1
.
This problematic inconsistency is the subject of GitHub issue #20170.
[1] In a nutshell, ConciseView
prints a single-line error description of the form <source>: <description>
, e.g. Get-Item: Cannot find path 'c:\tmp\NoSuch' because it does not exist.
, whereas NormalView
prints a multiline description that includes metadata about the error, e.g., Get-Item : Cannot find path 'C:\tmp\NoSuch' because it does not exist. <newline>At C:\tmp\demo.ps1:5 char:1 <newline>+ Get-Item NoSuch <newline>+ ~~~~~~~~~~~~~~~ <newline> + CategoryInfo : ObjectNotFound: (C:\tmp\NoSuch:String) [Get-Item], ItemNotFoundException <newline> + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand
However, note that even in 'ConciseView'
errors are situationally multiline, namely if they are reported during execution of a script file (*.ps1
) - as opposed to errors reported from a function or cmdlet invoked from outside a script file, such as interactively. Errors reported from a script file are preceded by four lines containing location information, i.e. the path of the script file, the number and text of the line containing the error-triggering statement, similar to the first four lines shown for 'NormalView'
above, except for the error description, which follows the location information in 'ConciseView'
.
More detailed error information can always be obtained later in PowerShell 7, via the Get-Error
cmdlet; in Windows PowerShell, you can use something like $Error[0] | Format-List -Force
.