TheGlassSword
TheGlassSword

Reputation: 13

Strange behavior of "&>" in subprocess Popen

I've noticed that the "&>" operator within a subprocess.Popen() call immediately provides a return code, even in scenarios where it intuitively shouldn't be. To illustrate:

>>> import subprocess
>>> a = subprocess.Popen("sleep 5 > a.txt", shell = True)
>>> print(a.poll()) # immediately try printing
None
>>> print(a.poll()) # wait 5 seconds
0
>>> a = subprocess.Popen("sleep 5 &> a.txt", shell = True)
>>> print(a.poll()) # immediately try printing
0
>>> a = subprocess.Popen("sleep 5 > a.txt 2>&1", shell = True) # this should be the same as using &>
>>> print(a.poll()) # immediately try printing
None
>>> print(a.poll()) # wait 5 seconds
0

I'm running this on Python 3.5.2. My machine runs bash by default.

Does anyone know why subprocess isn't supporting the correct "&>" behavior here?

Upvotes: 1

Views: 99

Answers (2)

chepner
chepner

Reputation: 531155

Your default login shell is bash. subprocess.Popen, though, uses the system default shell /bin/sh, which does not recognize the &> operator. If you want to force it to use bash, use the executable option:

a = subprocess.Popen("sleep 5 &> a.txt", shell=True, executable="/bin/bash")

Alternatively, you can handle the redirection yourself in Python:

with open("a.txt", "w") as fh:
    a = subprocess.Popen(["sleep", "5"], stdout=fh, stderr=fh)

Upvotes: 2

randomir
randomir

Reputation: 18697

That's because &> is a bashism, and Popen by default uses /bin/sh when shell=True.

From the subprocess.Popen docs:

The executable argument specifies a replacement program to execute. It is very seldom needed. When shell=False, executable replaces the program to execute specified by args. However, the original args is still passed to the program. Most programs treat the program specified by args as the command name, which can then be different from the program actually executed. On POSIX, the args name becomes the display name for the executable in utilities such as ps. If shell=True, on POSIX the executable argument specifies a replacement shell for the default /bin/sh.

The fix is to specify the executable parameter explicitly, like:

subprocess.Popen("sleep 5 &> a.txt", shell=True, executable='/bin/bash')

Upvotes: 2

Related Questions