400

I want to use subprocess.check_output() with ps -A | grep 'process_name'. I tried various solutions but so far nothing worked. Can someone guide me how to do it?

3

9 Answers 9

641

To use a pipe with the subprocess module, you can pass shell=True but be aware of the Security Considerations. It is discouraged using shell=True. In most cases there are better solutions for the same problem.

However, this isn't really advisable for various reasons, not least of which is security. Instead, create the ps and grep processes separately, and pipe the output from one into the other, like so:

ps = subprocess.Popen(('ps', '-A'), stdout=subprocess.PIPE)
output = subprocess.check_output(('grep', 'process_name'), stdin=ps.stdout)
ps.wait()

In your particular case, however, the simple solution is to call subprocess.check_output(('ps', '-A')) and then str.find on the output.

Sign up to request clarification or add additional context in comments.

16 Comments

+1 for separating the output/input to avoid using shell=True
Don't forget, error subprocess.CalledProcessError: Command '('grep', 'process_name')' returned non-zero exit status 1 just means that nothing was found by grep, so it's normal behaviour.
Image
Why do we need the ps.wait() for when we already have the output. ps.wait.__doc__ waits for the child to terminate but the content of the child seems already placed into the output variable
@MakisH You're looking at string.find, which has been deprecated in favor of str.find (i.e., the method find on str objects).
note: if grep dies prematurely; ps may hang indefinitely if it produces enough output to fill its OS pipe buffer (because you haven't called ps.stdout.close() in the parent). Swap the starting order, to avoid it
|
89

Or you can always use the communicate method on the subprocess objects.

cmd = "ps -A|grep 'process_name'"
ps = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
output = ps.communicate()[0]
print(output)

The communicate method returns a tuple of the standard output and the standard error.

7 Comments

I think using communicate is better than wait. There is such warning: "This will deadlock when using stdout=PIPE and/or stderr=PIPE and the child process generates enough output to a pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that."
To clarify Paolo's comment above, the warning is for wait, not for communicate - i.e. it's the reason he says communicate is better.
The output of ps.communicate()[0] in python3 returns a bytes object.
You are reinventing subprocess.check_output, not too poorly but unattractively. As the documentation suggests, you should avoid the low-level Popen when the library already provides higher-level functions which take care of all this plumbing in a single line of code, often with better behavior for boundary conditions.
@JvO. That makes very little sense in this context and will probably only confuse you and require that you actively separate stdout from stderr, which you may not even be able to do reliably. Why not trust that communicate will do what it is designed to do? It would be better to set both stdout and stderr to subprocess.PIPE. The communicate method will then return a tuple with stdout at index 0 and stderr at index 1. That is how it should be done.
|
56

Using input from subprocess.run you can pass the output of one command into a second one.

import subprocess
    
ps = subprocess.run(['ps', '-A'], check=True, capture_output=True)
processNames = subprocess.run(['grep', 'process_name'],
                              input=ps.stdout, capture_output=True)
print(processNames.stdout.decode('utf-8').strip())

9 Comments

NOTE: capture_output will only work for Python 3.7.9 and above.
what does check do and what's the purpose of capture_output?
@CervEd Both of these are clearly documented. capture_output is a shorthand for the option combination stdout=supprocess.PIPE, stderr=subprocess.PIPE and check=True raises an error if the subprocess did not return a success (zero) status.
@tripleee they are documented, somewhere in the unwieldy Python documentation, but there's no detail in the answer as to why they are included. check=True is for example not strictly necessary but capture_output=True is for the answer to work. The reason for using these options should be included as a part of the answer
A downside to this approach is that capture_output will read all of the process's stdout into memory. For small programs like ps, this may be fine, but for larger analysis pipelines this should be avoided.
|
32

See the documentation on setting up a pipeline using subprocess: http://docs.python.org/2/library/subprocess.html#replacing-shell-pipeline

I haven't tested the following code example but it should be roughly what you want:

query = "process_name"
ps_process = Popen(["ps", "-A"], stdout=PIPE)
grep_process = Popen(["grep", query], stdin=ps_process.stdout, stdout=PIPE)
ps_process.stdout.close()  # Allow ps_process to receive a SIGPIPE if grep_process exits.
output = grep_process.communicate()[0]

2 Comments

Upon checking this failed, see the answer below by Taymon for something that works without mucking around
subprocess.check_output doesn't appear to exist in Python 2.6.9
3

Also, try to use 'pgrep' command instead of 'ps -A | grep 'process_name'

1 Comment

if you want get process id, obviously
3

You can try the pipe functionality in sh.py:

import sh
print sh.grep(sh.ps("-ax"), "process_name")

4 Comments

The link is dead.
Not anymore, link updated.
It's dead again!
Image
This is the link: github.com/amoffat/sh
1
command = "ps -A | grep 'process_name'"
output = subprocess.check_output(["bash", "-c", command])

7 Comments

Why not use shell=True and let that prepend ['sh', '-c']? Nothing in this code requires bash. (That said, it's significantly better practice to avoid using a shell at all; this use case is a reasonable one, but as soon as arguments start to get parameterized -- like taking the process_name as a parameter -- security concerns come in).
It's useful in that you don't have to split the string, which gets complicated when you have quoted white space.
Huh? subprocess.check_output(command, shell=True) doesn't require you to split the string. Popen converts any string into a list containing only that string -- thus, [command] -- so with shell=True you get ['sh', '-c'] prepended to that list, so you end up with ['sh', '-c', command], exactly what your code does here except for the sh/bash difference.
...for that matter, if you did try to split the string into a list as well as using shell=True, only the first element of that list would be treated as code; you'd get something like ['sh', '-c', 'ps', '-A', '|', 'grep', 'process_name']. That's not a useful thing to do: when invoked that way, the shell runs ps with $0 being -A, $1 being |, etc... but since the command ps doesn't look at $0, $1, etc., all that extra content is simply ignored.
If you read Lib/subprocess.py, you'll see that there literally is no difference between subprocess.check_output(["sh", "-c", command]) and subprocess.check_output(command, shell=True). The code is clear and simple -- this is not a place where there can be a devil hiding in the details somewhere.
|
1

I'm answering an old question, because nobody mentioned the shlex. In Unix, you can construct a command using pipes safely with the shlex.join(), like the following example.

import shlex
import subprocess

print(subprocess.check_output(
    shlex.join(['ps', '-ef']) + ' | ' +
    shlex.join(['grep', 'ps -ef']), # The arguments will be quoted.
    shell=True
).decode('utf-8'))

Comments

0

I think that launching a shell just to enjoy the pipelining is not as elegant as it could be.

The following code uses native subprocess pipeline support and it works indeed.

You could easily modify it to add more than two processes to the pipeline.

#!/usr/bin/env python3

import subprocess


def ps_grep(pattern):
    # First command-line
    ps_command = ["ps", "-A"]

    # Second command-line
    grep_command = ["grep", pattern]

    # Launch first process
    ps_process = subprocess.Popen(ps_command, stdout=subprocess.PIPE)

    # Launch second process and connect it to the first one
    grep_process = subprocess.Popen(
        grep_command, stdin=ps_process.stdout, stdout=subprocess.PIPE
    )

    # Let stream flow between them
    output, _ = grep_process.communicate()

    return output.decode()


if __name__ == "__main__":
    print(ps_grep("python"))

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.