Honor -OutputFormat if specified in noninteractive, redirected, encoded command used with pwsh (#8115)

[Breaking Change]

There is specific code that sets the `OutputFormat` to xml if pwsh is run non-interactive, with redirected output, and the command was encoded.  However, it ignored whether OutputFormat was specified.  Fix is to track whether `-OutputFormat` was used and respect that value rather than defaulting to xml.

Fix https://github.com/PowerShell/PowerShell/issues/5912
This commit is contained in:
Steve Lee 2018-10-29 17:51:21 -07:00 committed by Travis Plunk
parent 8215914b46
commit 1aa5bb3576
3 changed files with 33 additions and 6 deletions

View file

@ -349,6 +349,16 @@ namespace Microsoft.PowerShell
}
}
internal bool OutputFormatSpecified
{
get
{
Dbg.Assert(_dirty, "Parse has not been called yet");
return _outputFormatSpecified;
}
}
internal Serialization.DataFormat InputFormat
{
get
@ -475,7 +485,7 @@ namespace Microsoft.PowerShell
// Current user policy takes precedence.
var consoleSessionSetting = Utils.GetPolicySetting<ConsoleSessionConfiguration>(Utils.CurrentUserThenSystemWideConfig);
return (consoleSessionSetting?.EnableConsoleSessionConfiguration == true && !string.IsNullOrEmpty(consoleSessionSetting?.ConsoleSessionConfigurationName)) ?
return (consoleSessionSetting?.EnableConsoleSessionConfiguration == true && !string.IsNullOrEmpty(consoleSessionSetting?.ConsoleSessionConfigurationName)) ?
consoleSessionSetting.ConsoleSessionConfigurationName : string.Empty;
}
@ -493,7 +503,7 @@ namespace Microsoft.PowerShell
Dbg.Assert(args != null, "Argument 'args' to EarlyParseHelper should never be null");
return;
}
bool noexitSeen = false;
for (int i = 0; i < args.Length; ++i)
{
@ -587,7 +597,7 @@ namespace Microsoft.PowerShell
Dbg.Assert(smallestUnambiguousMatch.Trim().ToLowerInvariant() == smallestUnambiguousMatch, "match should be normalized to lowercase w/ no outside whitespace");
Dbg.Assert(match.Contains(smallestUnambiguousMatch), "sUM should be a substring of match");
return (match.Trim().ToLowerInvariant().IndexOf(switchKey, StringComparison.Ordinal) == 0 &&
return (match.Trim().ToLowerInvariant().IndexOf(switchKey, StringComparison.Ordinal) == 0 &&
switchKey.Length >= smallestUnambiguousMatch.Length);
}
@ -849,6 +859,7 @@ namespace Microsoft.PowerShell
else if (MatchSwitch(switchKey, "outputformat", "o") || MatchSwitch(switchKey, "of", "o"))
{
ParseFormat(args, ref i, ref _outFormat, CommandLineParameterParserStrings.MissingOutputFormatParameter);
_outputFormatSpecified = true;
}
else if (MatchSwitch(switchKey, "inputformat", "in") || MatchSwitch(switchKey, "if", "if"))
{
@ -1347,6 +1358,7 @@ namespace Microsoft.PowerShell
private uint _exitCode = ConsoleHost.ExitCodeSuccess;
private bool _dirty;
private Serialization.DataFormat _outFormat = Serialization.DataFormat.Text;
private bool _outputFormatSpecified = false;
private Serialization.DataFormat _inFormat = Serialization.DataFormat.Text;
private Collection<CommandParameter> _collectedArgs = new Collection<CommandParameter>();
private string _file;

View file

@ -1194,6 +1194,7 @@ namespace Microsoft.PowerShell
}
internal WrappedSerializer.DataFormat OutputFormat { get; private set; }
internal bool OutputFormatSpecified { get; private set; }
internal WrappedSerializer.DataFormat InputFormat { get; private set; }
@ -1203,9 +1204,9 @@ namespace Microsoft.PowerShell
{
WrappedDeserializer.DataFormat format = OutputFormat;
//If this shell is invoked in minishell interop mode and error is redirected,
//always write data in error stream in xml format.
if (IsInteractive == false && Console.IsErrorRedirected && _wasInitialCommandEncoded)
// If this shell is invoked in non-interactive, error is redirected, and OutputFormat was not
// specified write data in error stream in xml format assuming PowerShell->PowerShell usage.
if (!OutputFormatSpecified && IsInteractive == false && Console.IsErrorRedirected && _wasInitialCommandEncoded)
{
format = Serialization.DataFormat.XML;
}
@ -1315,6 +1316,7 @@ namespace Microsoft.PowerShell
}
OutputFormat = cpp.OutputFormat;
OutputFormatSpecified = cpp.OutputFormatSpecified;
InputFormat = cpp.InputFormat;
_wasInitialCommandEncoded = cpp.WasInitialCommandEncoded;

View file

@ -298,6 +298,19 @@ Describe "ConsoleHost unit tests" -tags "Feature" {
# Join (multiple lines) and remove whitespace (we don't care about spacing) to verify we converted to string (by generating a table)
-join (& $powershell -noprofile -outputFormat text { [PSCustomObject]@{X=10;Y=20} }) -replace "\s","" | Should -Be "XY--1020"
}
It "errors are in text if error is redirected, encoded command, non-interactive, and outputformat specified" {
$p = [Diagnostics.Process]::new()
$p.StartInfo.FileName = "pwsh"
$encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("throw 'boom'"))
$p.StartInfo.Arguments = "-EncodedCommand $encoded -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -OutputFormat text"
$p.StartInfo.UseShellExecute = $false
$p.StartInfo.RedirectStandardError = $true
$p.Start() | Out-Null
$out = $p.StandardError.ReadToEnd()
$out | Should -Not -BeNullOrEmpty
$out.Split([Environment]::NewLine)[0] | Should -BeExactly "boom"
}
}
Context "Redirected standard output" {