I'm executing several commands in a row, with several calls, in the same powershell session through python's subprocess. e.g. first defining a function, then utilizing said function in the same powershell session - all from python through subprocess' popen.
Some of the powershell commands are utilizing the "Write-Host" cmdlet with the -ForegroundColor parameter. I'd prefer to not have to change this and I am not aware of any other way to color text in powershell.
The output of my powershell session is piped to a file-handle and I would like to preserve the colors set by the -ForegroundColor parameter in the output file. The idea is, to be able to read the file later from powershell and display the whole output including colors.
So far I have not been able to pipe the color codes into the output file.
An MVP looks like this:
from subprocess import Popen, PIPE, call
import time
print("\n\ncoloring in the terminal works:")
print("--------------------------")
time.sleep(1)
process = Popen(['powershell.exe'], stdin=PIPE)
process.stdin.write(b'Write-Host -ForegroundColor Red "TEST"\r\n')
process.stdin.flush()
process.stdin.close()
time.sleep(1)
print("\n\ncoloring when writing to a file and reading output from that file again")
print("------------------------------------------------------------------------")
with open('./out.txt', 'wb') as f:
process = Popen(['powershell.exe'], stdin=PIPE, stdout=f)
process.stdin.write(b'Write-Host -ForegroundColor Red "TEST"\r\n')
process.stdin.flush()
process.stdin.close()
time.sleep(1)
call(['powershell.exe', 'cat', './out.txt'])
print("\n\n---------------")
print("and we're done.\n")
Is there a way to preserve the coloring of the output?
I'd prefer to not have to change this and I am not aware of any other way to color text in powershell.
You can use ANSI/VT escape sequences, which are ESC-based sequences that must be embedded in the output string.
Here's your Python code adapted to use this technique:
import subprocess, time
with open('./out.txt', 'wb') as f:
process = subprocess.Popen(['powershell.exe', '-NoProfile', '-NoLogo'], stdin=subprocess.PIPE, stdout=f)
process.stdin.write(b'Write-Host "\x1b[31mTEST\x1b[m"\r\n')
process.stdin.flush()
process.stdin.close()
time.sleep(1)
subprocess.call(['powershell.exe', '-NoProfile', 'Get-Content', './out.txt'])
Note:
In the "\x1b[31mTEST\x1b[m"
string, escape sequence \x1b[31m
switches to red, \x1b[m
reverts to the default colors.
The equivalent PowerShell string literal is:
Windows PowerShell:
"$([char] 27)[31mRed$([char] 27)[m"
# Alternative via the -f operator:
'{0}[31mRed{0}[m' -f [char] 27
PowerShell (Core) 7 makes this easier, because it now supports escape sequence `e
, which expands to an ESC character:
"`e[31mRed`e[m"
Additionally, PowerShell 7 offers access to these escape sequences via descriptive property names exposed via the $PSStyle
preference variable:
"$($PSStyle.Foreground.Red)Red$($PSStyle.Reset)"
# Alternative via the -f operator:
'{0}Red{1}' -f $PSStyle.Foreground.Red, $PSStyle.Reset
A Write-Host
solution is not currently possible:
On Windows, Write-Host
uses the legacy console Windows APIs, which do not embed coloring information in the strings they output, and therefore by definition can only work when printing to the console - capturing colored text in a file is therefore not possible; ditto for further processing in a pipeline.
Even on Unix-like platforms, where PowerShell 7 does use ANSI/VT escape sequences, Write-Host
seemingly unconditionally omits them when not printing directly to the terminal.
Arguably, however, PowerShell 7:
should make Write-Host
use ANSI/VT sequences on Windows too.
should respect the active $PSStyle.OutputRendering
mode and, when it is set to 'ANSI'
, embed the ANSI/VT sequences unconditionally.
The above may get implemented in a future PowerShell 7 version - see GitHub issue #21168