diff --git a/.vsts-ci/linux.yml b/.vsts-ci/linux.yml
index 5218a20cf9..1236a0e278 100644
--- a/.vsts-ci/linux.yml
+++ b/.vsts-ci/linux.yml
@@ -35,6 +35,7 @@ variables:
POWERSHELL_TELEMETRY_OPTOUT: 1
# Avoid expensive initialization of dotnet cli, see: https://donovanbrown.com/post/Stop-wasting-time-during-NET-Core-builds
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
+ __SuppressAnsiEscapeSequences: 1
resources:
- repo: self
diff --git a/.vsts-ci/mac.yml b/.vsts-ci/mac.yml
index 1df0bcf611..b048a248bb 100644
--- a/.vsts-ci/mac.yml
+++ b/.vsts-ci/mac.yml
@@ -37,6 +37,7 @@ variables:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
# Turn off Homebrew analytics
HOMEBREW_NO_ANALYTICS: 1
+ __SuppressAnsiEscapeSequences: 1
resources:
- repo: self
diff --git a/.vsts-ci/windows-daily.yml b/.vsts-ci/windows-daily.yml
index 65390776c1..7b9408eec7 100644
--- a/.vsts-ci/windows-daily.yml
+++ b/.vsts-ci/windows-daily.yml
@@ -34,6 +34,7 @@ variables:
POWERSHELL_TELEMETRY_OPTOUT: 1
# Avoid expensive initialization of dotnet cli, see: https://donovanbrown.com/post/Stop-wasting-time-during-NET-Core-builds
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
+ __SuppressAnsiEscapeSequences: 1
resources:
- repo: self
diff --git a/.vsts-ci/windows.yml b/.vsts-ci/windows.yml
index 2881a6a4fb..84a9435d04 100644
--- a/.vsts-ci/windows.yml
+++ b/.vsts-ci/windows.yml
@@ -34,6 +34,7 @@ variables:
POWERSHELL_TELEMETRY_OPTOUT: 1
# Avoid expensive initialization of dotnet cli, see: https://donovanbrown.com/post/Stop-wasting-time-during-NET-Core-builds
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
+ __SuppressAnsiEscapeSequences: 1
resources:
- repo: self
diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs
index f1052cbea5..584a3ebe6c 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs
@@ -256,7 +256,7 @@ namespace Microsoft.PowerShell.Commands
}
///
- /// Returns the string representation of the match object with the matched line passed
+ /// Returns the string representation of the match object with the matched line passed
/// in as and trims the path to be relative to
/// the argument.
///
@@ -315,10 +315,10 @@ namespace Microsoft.PowerShell.Commands
/// The matched line with matched text inverted.
private string EmphasizeLine()
{
- const string InvertColorsVT100 = "\u001b[7m";
- const string ResetVT100 = "\u001b[0m";
+ string invertColorsVT100 = VTUtility.GetEscapeSequence(VTUtility.VT.Inverse);
+ string resetVT100 = VTUtility.GetEscapeSequence(VTUtility.VT.Reset);
- char[] chars = new char[(_matchIndexes.Count * (InvertColorsVT100.Length + ResetVT100.Length)) + Line.Length];
+ char[] chars = new char[(_matchIndexes.Count * (invertColorsVT100.Length + resetVT100.Length)) + Line.Length];
int lineIndex = 0;
int charsIndex = 0;
for (int i = 0; i < _matchIndexes.Count; i++)
@@ -329,8 +329,8 @@ namespace Microsoft.PowerShell.Commands
lineIndex = _matchIndexes[i];
// Adds opening vt sequence
- InvertColorsVT100.CopyTo(0, chars, charsIndex, InvertColorsVT100.Length);
- charsIndex += InvertColorsVT100.Length;
+ invertColorsVT100.CopyTo(0, chars, charsIndex, invertColorsVT100.Length);
+ charsIndex += invertColorsVT100.Length;
// Adds characters being emphasized
Line.CopyTo(lineIndex, chars, charsIndex, _matchLengths[i]);
@@ -338,8 +338,8 @@ namespace Microsoft.PowerShell.Commands
charsIndex += _matchLengths[i];
// Adds closing vt sequence
- ResetVT100.CopyTo(0, chars, charsIndex, ResetVT100.Length);
- charsIndex += ResetVT100.Length;
+ resetVT100.CopyTo(0, chars, charsIndex, resetVT100.Length);
+ charsIndex += resetVT100.Length;
}
// Adds remaining characters in line
@@ -1294,8 +1294,8 @@ namespace Microsoft.PowerShell.Commands
private int _postContext = 0;
// When we are in Raw mode or pre- and postcontext are zero, use the _noContextTracker, since we will not be needing trackedLines.
- private IContextTracker GetContextTracker() => (Raw || (_preContext == 0 && _postContext == 0))
- ? _noContextTracker
+ private IContextTracker GetContextTracker() => (Raw || (_preContext == 0 && _postContext == 0))
+ ? _noContextTracker
: new ContextTracker(_preContext, _postContext);
// This context tracker is only used for strings which are piped
@@ -1322,6 +1322,12 @@ namespace Microsoft.PowerShell.Commands
///
protected override void BeginProcessing()
{
+ string suppressVt = Environment.GetEnvironmentVariable("__SuppressAnsiEscapeSequences");
+ if (!string.IsNullOrEmpty(suppressVt))
+ {
+ NoEmphasis = true;
+ }
+
if (!SimpleMatch)
{
RegexOptions regexOptions = CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase;
diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs
index 4bfecc19da..e8c2f2e7bd 100644
--- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs
+++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs
@@ -741,35 +741,18 @@ namespace System.Management.Automation.Runspaces
$maxDepth = 10
$ellipsis = ""`u{2026}""
$resetColor = ''
- if ($Host.UI.SupportsVirtualTerminal) {
- $resetColor = ""`e[0m""
+ if ($Host.UI.SupportsVirtualTerminal -and !(Test-Path env:__SuppressAnsiEscapeSequences)) {
+ $resetColor = [System.Management.Automation.VTUtility]::GetEscapeSequence(
+ [System.Management.Automation.VTUtility+VT]::Reset
+ )
}
- function Get-VT100Color([string] $color) {
- if (! $Host.UI.SupportsVirtualTerminal) {
+ function Get-VT100Color([ConsoleColor] $color) {
+ if (!$Host.UI.SupportsVirtualTerminal -or (Test-Path env:__SuppressAnsiEscapeSequences)) {
return ''
}
- $colors = @{
- 'Black' = ""`e[2;30m""
- 'DarkRed' = ""`e[2;31m""
- 'DarkGreen' = ""`e[2;32m""
- 'DarkYellow' = ""`e[2;33m""
- 'DarkBlue' = ""`e[2;34m""
- 'DarkMagenta' = ""`e[2;35m""
- 'DarkCyan' = ""`e[2;36m""
- 'Gray' = ""`e[2;37m""
- 'DarkGray' = ""`e[1;30m""
- 'Red' = ""`e[1;31m""
- 'Green' = ""`e[1;32m""
- 'Yellow' = ""`e[1;33m""
- 'Blue' = ""`e[1;34m""
- 'Magenta' = ""`e[1;35m""
- 'Cyan' = ""`e[1;36m""
- 'White' = ""`e[1;37m""
- }
-
- return $colors[$color]
+ return [System.Management.Automation.VTUtility]::GetEscapeSequence($color)
}
function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
@@ -981,35 +964,18 @@ namespace System.Management.Automation.Runspaces
Set-StrictMode -Off
$resetColor = ''
- if ($Host.UI.SupportsVirtualTerminal) {
- $resetColor = ""`e[0m""
+ if ($Host.UI.SupportsVirtualTerminal -and !(Test-Path env:__SuppressAnsiEscapeSequences)) {
+ $resetColor = [System.Management.Automation.VTUtility]::GetEscapeSequence(
+ [System.Management.Automation.VTUtility+VT]::Reset
+ )
}
- function Get-VT100Color([string] $color) {
- if (! $Host.UI.SupportsVirtualTerminal) {
+ function Get-VT100Color([ConsoleColor] $color) {
+ if (!$Host.UI.SupportsVirtualTerminal -or (Test-Path env:__SuppressAnsiEscapeSequences)) {
return ''
}
- $colors = @{
- 'Black' = ""`e[2;30m""
- 'DarkRed' = ""`e[2;31m""
- 'DarkGreen' = ""`e[2;32m""
- 'DarkYellow' = ""`e[2;33m""
- 'DarkBlue' = ""`e[2;34m""
- 'DarkMagenta' = ""`e[2;35m""
- 'DarkCyan' = ""`e[2;36m""
- 'Gray' = ""`e[2;37m""
- 'DarkGray' = ""`e[1;30m""
- 'Red' = ""`e[1;31m""
- 'Green' = ""`e[1;32m""
- 'Yellow' = ""`e[1;33m""
- 'Blue' = ""`e[1;34m""
- 'Magenta' = ""`e[1;35m""
- 'Cyan' = ""`e[1;36m""
- 'White' = ""`e[1;37m""
- }
-
- return $colors[$color]
+ return [System.Management.Automation.VTUtility]::GetEscapeSequence($color)
}
# return length of string sans VT100 codes
diff --git a/src/System.Management.Automation/utils/VTUtils.cs b/src/System.Management.Automation/utils/VTUtils.cs
new file mode 100644
index 0000000000..2ff110dd42
--- /dev/null
+++ b/src/System.Management.Automation/utils/VTUtils.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Collections.Generic;
+
+namespace System.Management.Automation
+{
+ ///
+ /// Class to help with VT escape sequences.
+ ///
+ public sealed class VTUtility
+ {
+ ///
+ /// Available VT escape codes other than colors.
+ ///
+ public enum VT
+ {
+ /// Reset the text style.
+ Reset,
+
+ /// Invert the foreground and background colors.
+ Inverse
+ }
+
+ private static readonly Dictionary ConsoleColors = new Dictionary
+ {
+ { ConsoleColor.Black, "\x1b[2;30m" },
+ { ConsoleColor.Gray, "\x1b[2;37m" },
+ { ConsoleColor.Red, "\x1b[1;31m" },
+ { ConsoleColor.Green, "\x1b[1;32m" },
+ { ConsoleColor.Yellow, "\x1b[1;33m" },
+ { ConsoleColor.Blue, "\x1b[1;34m" },
+ { ConsoleColor.Magenta, "\x1b[1;35m" },
+ { ConsoleColor.Cyan, "\x1b[1;36m" },
+ { ConsoleColor.White, "\x1b[1;37m" },
+ { ConsoleColor.DarkRed, "\x1b[2;31m" },
+ { ConsoleColor.DarkGreen, "\x1b[2;32m" },
+ { ConsoleColor.DarkYellow, "\x1b[2;33m" },
+ { ConsoleColor.DarkBlue, "\x1b[2;34m" },
+ { ConsoleColor.DarkMagenta, "\x1b[2;35m" },
+ { ConsoleColor.DarkCyan, "\x1b[2;36m" },
+ { ConsoleColor.DarkGray, "\x1b[1;30m" },
+ };
+
+ private static readonly Dictionary VTCodes = new Dictionary
+ {
+ { VT.Reset, "\x1b[0m" },
+ { VT.Inverse, "\x1b[7m" }
+ };
+
+ ///
+ /// Return the VT escape sequence for a ConsoleColor.
+ ///
+ ///
+ /// The ConsoleColor to return the equivalent VT escape sequence.
+ ///
+ ///
+ /// The requested VT escape sequence.
+ ///
+ public static string GetEscapeSequence(ConsoleColor color)
+ {
+ string value = string.Empty;
+ ConsoleColors.TryGetValue(color, out value);
+ return value;
+ }
+
+ ///
+ /// Return the VT escape sequence for a supported VT enum value.
+ ///
+ ///
+ /// The VT code to return the VT escape sequence.
+ ///
+ ///
+ /// The requested VT escape sequence.
+ ///
+ public static string GetEscapeSequence(VT vt)
+ {
+ string value = string.Empty;
+ VTCodes.TryGetValue(vt, out value);
+ return value;
+ }
+ }
+}
diff --git a/test/powershell/Language/Scripting/SuppressAnsiEscapeSequence.Tests.ps1 b/test/powershell/Language/Scripting/SuppressAnsiEscapeSequence.Tests.ps1
new file mode 100644
index 0000000000..c46674ff3a
--- /dev/null
+++ b/test/powershell/Language/Scripting/SuppressAnsiEscapeSequence.Tests.ps1
@@ -0,0 +1,89 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+Describe '$env:__SuppressAnsiEscapeSequences tests' -Tag CI {
+ BeforeAll {
+ $originalSuppressPref = $env:__SuppressAnsiEscapeSequences
+ }
+
+ AfterAll {
+ $env:__SuppressAnsiEscapeSequences = $originalSuppressPref
+ }
+
+
+ Context 'Allow Escape Sequences' {
+ BeforeAll {
+ Remove-Item env:__SuppressAnsiEscapeSequences -ErrorAction Ignore
+ }
+
+ It 'Select-String emits VT' {
+ "select this string" | Select-String 'this' | Out-String |Should -BeLikeExactly "*`e*"
+ }
+
+ It 'ConciseView emits VT' {
+ $oldErrorView = $ErrorView
+
+ try {
+ $ErrorView = 'ConciseView'
+ Invoke-Expression '1/d'
+ }
+ catch {
+ $e = $_
+ }
+ finally {
+ $ErrorView = $oldErrorView
+ }
+
+ $e | Out-String | Should -BeLikeExactly "*`e*"
+ }
+
+ It 'Get-Error emits VT' {
+ try {
+ 1/0
+ }
+ catch {
+ # ignore
+ }
+
+ Get-Error | Out-String | Should -BeLikeExactly "*`e*"
+ }
+ }
+
+ Context 'No Escape Sequences' {
+ BeforeAll {
+ $env:__SuppressAnsiEscapeSequences = 1
+ }
+
+ It 'Select-String does not emit VT' {
+ "select this string" | Select-String 'this' | Out-String | Should -Not -BeLikeExactly "*`e*"
+ }
+
+ It 'ConciseView does not emit VT' {
+ $oldErrorView = $ErrorView
+
+ try {
+ $ErrorView = 'ConciseView'
+ Invoke-Expression '1/d'
+ }
+ catch {
+ $e = $_
+ }
+ finally {
+ $ErrorView = $oldErrorView
+ }
+
+ $e | Out-String | Should -Not -BeLikeExactly "*`e*"
+ }
+
+ It 'Get-Error does not emit VT' {
+ try {
+ 1/0
+ }
+ catch {
+ # ignore
+ }
+
+ Get-Error | Out-String | Should -Not -BeLikeExactly "*`e*"
+ }
+ }
+}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1
index 8a73ddfe8b..9b5bc1b966 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-String.Tests.ps1
@@ -7,6 +7,10 @@ Describe "Select-String" -Tags "CI" {
$currentDirectory = $pwd.Path
}
+ AfterAll {
+ Push-Location $currentDirectory
+ }
+
Context "String actions" {
$testinputone = "hello","Hello","goodbye"
$testinputtwo = "hello","Hello"
@@ -76,47 +80,47 @@ Describe "Select-String" -Tags "CI" {
}
it "Should output a string with the first match highlighted" {
- if ($Host.UI.SupportsVirtualTerminal)
+ if ($Host.UI.SupportsVirtualTerminal -and !(Test-Path env:__SuppressAnsiEscapeSequences))
{
$result = $testinputone | Select-String -Pattern "l" | Out-String
- $result | Should -Be "`nhe`e[7ml`e[0mlo`nHe`e[7ml`e[0mlo`n`n"
+ $result | Should -Be "${nl}he`e[7ml`e[0mlo${nl}He`e[7ml`e[0mlo${nl}${nl}"
}
else
{
$result = $testinputone | Select-String -Pattern "l" | Out-String
- $result | Should -Be "`nhello`nHello`n`n"
+ $result | Should -Be "${nl}hello${nl}Hello${nl}${nl}"
}
}
it "Should output a string with all matches highlighted when AllMatch is used" {
- if ($Host.UI.SupportsVirtualTerminal)
+ if ($Host.UI.SupportsVirtualTerminal -and !(Test-Path env:__SuppressAnsiEscapeSequences))
{
$result = $testinputone | Select-String -Pattern "l" -AllMatch | Out-String
- $result | Should -Be "`nhe`e[7ml`e[0m`e[7ml`e[0mo`nHe`e[7ml`e[0m`e[7ml`e[0mo`n`n"
+ $result | Should -Be "${nl}he`e[7ml`e[0m`e[7ml`e[0mo${nl}He`e[7ml`e[0m`e[7ml`e[0mo${nl}${nl}"
}
else
{
$result = $testinputone | Select-String -Pattern "l" -AllMatch | Out-String
- $result | Should -Be "`nhello`nHello`n`n"
+ $result | Should -Be "${nl}hello${nl}Hello${nl}${nl}"
}
}
it "Should output a string with the first match highlighted when SimpleMatch is used" {
- if ($Host.UI.SupportsVirtualTerminal)
+ if ($Host.UI.SupportsVirtualTerminal -and !(Test-Path env:__SuppressAnsiEscapeSequences))
{
$result = $testinputone | Select-String -Pattern "l" -SimpleMatch | Out-String
- $result | Should -Be "`nhe`e[7ml`e[0mlo`nHe`e[7ml`e[0mlo`n`n"
+ $result | Should -Be "${nl}he`e[7ml`e[0mlo${nl}He`e[7ml`e[0mlo${nl}${nl}"
}
else
{
$result = $testinputone | Select-String -Pattern "l" -SimpleMatch | Out-String
- $result | Should -Be "`nhello`nHello`n`n"
+ $result | Should -Be "${nl}hello${nl}Hello${nl}${nl}"
}
}
it "Should output a string without highlighting when NoEmphasis is used" {
$result = $testinputone | Select-String -Pattern "l" -NoEmphasis | Out-String
- $result | Should -Be "`nhello`nHello`n`n"
+ $result | Should -Be "${nl}hello${nl}Hello${nl}${nl}"
}
it "Should return an array of matching strings without virtual terminal sequences" {
@@ -247,8 +251,4 @@ Describe "Select-String" -Tags "CI" {
Select-String second $testInputFile -Raw -Context 2,2 | Should -BeExactly $expected
}
}
-
- AfterAll {
- Push-Location $currentDirectory
- }
}