digitaLink digitaLink - 3 years ago 226
Python Question

No such file or Directory Error with subprocess.call in Python

In general, I am trying to use Bash to read from the command line instead of Python so that I have tab-completion functionality. I want to do this in the simplest way possible. However, I am having trouble getting the following code to work and I want to understand what is causing the issue.

Python Script:

from subprocess import call
call(['read', '-ep', 'Path:', 'temporaryPath'])
print temporaryPath


Error Traceback:

Traceback (most recent call last):
File "tmp.py", line 2, in <module>
call(['read', '-ep', 'Path:', 'temporaryPath'])
File "/usr/lib64/python2.6/subprocess.py", line 478, in call
p = Popen(*popenargs, **kwargs)
File "/usr/lib64/python2.6/subprocess.py", line 642, in __init__
errread, errwrite)
File "/usr/lib64/python2.6/subprocess.py", line 1238, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory

Answer Source

You're trying to call read which is a shell builtin:

$ type read
read is a shell builtin

And this particular shell builtin have no equivalent program:

$ which read
$ 

So Python won't be able to find it in your PATH environment variable, according to strace:

[pid 17266] execve("/usr/local/bin/read", ["read", "-ep", "Path:", "temporaryPath"], [/* 70 vars */]) = -1 ENOENT (No such file or directory)
[pid 17266] execve("/usr/bin/read", ["read", "-ep", "Path:", "temporaryPath"], [/* 70 vars */]) = -1 ENOENT (No such file or directory)
[pid 17266] execve("/bin/read", ["read", "-ep", "Path:", "temporaryPath"], [/* 70 vars */]) = -1 ENOENT (No such file or directory)
[pid 17266] execve("/usr/local/games/read", ["read", "-ep", "Path:", "temporaryPath"], [/* 70 vars */]) = -1 ENOENT (No such file or directory)
[pid 17266] execve("/usr/games/read", ["read", "-ep", "Path:", "temporaryPath"], [/* 70 vars */]) = -1 ENOENT (No such file or directory)
[…]
[pid 17266] write(4, "OSError:", 8 <unfinished ...>

But if you explicitly ask Python to use a shell to execute your command, the shell itself will be able to run its builtin read:

$ python3
Python 3.5.3 (default, Jan 19 2017, 14:11:04) 
[GCC 6.3.0 20170118] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> subprocess.call('read', shell=True)
/bin/sh: 1: read: arg count
2
>>> subprocess.call('read foo', shell=True)
hello world
0

You now have a new problem: The shell builtin read is storing the read value as a shell variable, which will diseapear with the shell dying right after the call to subprocess.call.

Oh and in the read shell builtin you don't have completion neither. You should probably just use input if you want to interactively ask something to the user, or if no interaction are needed, just use argparse to parse what the user is giving as command line arguments, this way the user will have some shell completion while typing the arguments, typically not on the flags, as the user shell don't know them, but on the paths.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download