5

I'm experimenting with subprocess.run in Python 3.5. To chain two commands together, I would have thought that the following should work:

import subprocess

ps1 = subprocess.run(['ls'], universal_newlines=True, stdout=subprocess.PIPE)
ps2 = subprocess.run(['cowsay'], stdin=ps1.stdout)

However, this fails with:

AttributeError: 'str' object has no attribute 'fileno'

ps2 was expecting a file-like object, but the output of ps1 is a simple string.

Is there a way to chain commands together with subprocess.run?

2 Answers 2

6

Turns out that subprocess.run has an input argument to handle this:

ps1 = subprocess.run(['ls'], universal_newlines=True, stdout=subprocess.PIPE)
ps2 = subprocess.run(['cowsay'], universal_newlines=True, input=ps1.stdout)

Also, the following works as well, which doesn't use input:

ps1 = subprocess.run(['ls'], universal_newlines=True, stdout=subprocess.PIPE)
ps2 = subprocess.run(['cowsay', ps1.stdout], universal_newlines=True)
Sign up to request clarification or add additional context in comments.

Comments

6

subprocess.run() can't be used to implement ls | cowsay without the shell because it doesn't allow to run the individual commands concurrently: each subprocess.run() call waits for the process to finish that is why it returns CompletedProcess object (notice the word "completed" there). ps1.stdout in your code is a string that is why you have to pass it as input and not the stdin parameter that expects a file/pipe (valid .fileno()).

Either use the shell:

subprocess.run('ls | cowsay', shell=True)

Or use subprocess.Popen, to run the child processes concurrently:

from subprocess import Popen, PIPE

cowsay = Popen('cowsay', stdin=PIPE)
ls = Popen('ls', stdout=cowsay.stdin)
cowsay.communicate()
ls.wait()

See How do I use subprocess.Popen to connect multiple processes by pipes?

2 Comments

Thanks. I was aware of the Popen syntax but was specifically looking for how to chain commands with subprocess.run in Python 3.5+. And it is indeed possible to chain commands using the input argument to subprocess.run.
@ChrisClark: do you understand the difference between: ls | cowsay and output=$(ls); cowsay <<< "$output"?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.