15

It seems that using shell=True in the first process of a chain somehow drops the stdout from downstream tasks:

p1 = Popen(['echo','hello'], stdout=PIPE)
p2 = Popen('cat', stdin=p1.stdout, stdout=PIPE)
p2.communicate()
# outputs correctly ('hello\n', None)

Making the first process use shell=True kills the output somehow...

p1 = Popen(['echo','hello'], stdout=PIPE, shell=True)
p2 = Popen('cat', stdin=p1.stdout, stdout=PIPE)
p2.communicate()
# outputs incorrectly ('\n', None)

shell=True on the second process doesn't seem to matter. Is this expected behavior?

Jake Biesinger
  • 5,538
  • 2
  • 23
  • 25

1 Answers1

19

When you pass shell=True, Popen expects a single string argument, not a list. So when you do this:

p1 = Popen(['echo','hello'], stdout=PIPE, shell=True)

What happens is this:

execve("/bin/sh", ["/bin/sh", "-c", "echo", "hello"], ...)

That is, it calls sh -c "echo", and hello is effectively ignored (technically it becomes a positional argument to the shell). So the shell runs echo, which prints \n, which is why you see that in your output.

If you use shell=True, you need to do this:

p1 = Popen('echo hello', stdout=PIPE, shell=True)
Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
larsks
  • 277,717
  • 41
  • 399
  • 399
  • 3
    Thanks! For posterity, here's the [docs](http://docs.python.org/library/subprocess.html): On Unix, with shell=True: If args is a sequence, the first item specifies the command string, and any additional items will be treated as additional arguments to the shell itself. That is to say, Popen does the equivalent of: `Popen(['/bin/sh', '-c', args[0], args[1], ...])` – Jake Biesinger May 19 '12 at 04:33
  • 1
    very poorly documented, IMHO – Davide May 12 '17 at 20:14