I am looking to find the starttime of all chrome instances on my windows computer. In powershell, I can do this via get-process chrome | Format-Table StartTime
.
I want to do this in a python script and use the output of this. My code is below:
import subprocess
call = "powershell get-process chrome | powershell Format-Table ProcessName, StartTime"
process = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=None, shell=True)
outputs = process.communicate()
print(outputs)
The output for this command is ['']
, even with chrome open.
If I change call
to
call = "powershell get-process chrome"
this outputs the table, as expected. I think the error has to do with the pipeline operator.
Your own helpful answer is the quickest way to solve your problem based on your approach of implicitly calling via cmd.exe
, due to shell=True
(^
-escaping the |
protects it from up-front interpretation by cmd.exe
)
As also implied by the linked answer, don't use powershell
twice: pass a single command that is a PowerShell pipeline to a single call to powershell.exe
, the Windows PowerShell CLI, which implies use of the -Command
(-c
) parameter.[1]
While shell=True
is convenient in terms of syntax (providing a the entire command line as a single string), it isn't necessary, and causes overhead due to the extra cmd.exe
process that must be created - let alone the need for careful escaping, to prevent unwanted up-front interpretation of parts of the command line by cmd.exe
, as in the case at hand.
Therefore, consider calling powershell.exe
directly, in which case no escaping of |
is needed, but it is then best to pass the command as an array, whose first element is the target executable, and whose subsequent elements are the arguments, specified individually:[2]
# ...
# Construct the PowerShell CLI call as an *array*:
# The target executable, followed by the arguments to pass to it.
call = "powershell.exe", "-c", "Get-Process chrome | Format-Table ProcessName, StartTime"
# Make the call, but do NOT use `shell=True`
process = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=None)
# ...
As an aside:
subprocess.run()
function, which - except for advanced use cases such as asynchronous execution and providing stdin input dynamically, in response to the running process' output - is preferable to the lower-level subprocess.Popen
interface it is based on.[1] Note that, by contrast, pwsh
, the PowerShell (Core) CLI, now requires use of -Command
/ -c
.
[2] On Windows, you may still pass a single string that is the entire command line (using only "..."
for embedded quoting), but on Unix-like platforms using an array is a must in order to pass arguments. (However, in an argument-less invocation, the executable name or path may be passed as a string rather than as a one-element array on both platforms. Also, with shell=True
passing a single string even with embedded arguments works on both platforms, as that string becomes a single argument that is passed to the shell executable.)