python-3.xpowershellsubprocesstext-coloring

Preserving the colors when piping Powershell's write-host into a file, when called from python subprocess


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?


Solution

  • 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:


    A Write-Host solution is not currently possible: