If I run command
from /bin/bash
or /bin/sh
, I get a similar result:
nico@xantico:~$ command -v lualatex
/usr/local/texlive/2021/bin/x86_64-linux/lualatex
nico@xantico:~$ sh
$ command -v lualatex
/usr/local/texlive/2021/bin/x86_64-linux/lualatex
$
(return codes are 0 each time).
If I run it from python, it looks like I need to use shell=True
, though I do not understand why:
nico@xantico:~$ python
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> subprocess.run('command -v lualatex', shell=True)
/usr/local/texlive/2021/bin/x86_64-linux/lualatex
CompletedProcess(args='command -v lualatex', returncode=0)
>>> subprocess.run('command -v lualatex'.split())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/subprocess.py", line 493, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/lib/python3.8/subprocess.py", line 858, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/lib/python3.8/subprocess.py", line 1704, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'command'
Most suprisingly it raises a FileNotFoundError
, instead of a subprocess.CalledProcessError
.
Adding either or both of text=True
and check=True
does not change anything.
Using something else as command
does work as expected (either a CompletedProcess
with a returncode
of 0
, or a subprocess.CalledProcessError
). I've tried with printf
, echo
, ls
, cat
, which
...
So, what is so special with command
?
CodePudding user response:
command
is a built-in keyword for bash
and other shells. Most likely, your system has no actual /usr/bin/command
executable in its search PATH
.
subprocess.run('command -v lualatex', shell=True)
is roughly equivalent to subprocess.run(['bash', '-c', 'command -v lualatex'])
on systems with the bash
shell as default. Then bash
will interpret command
as its built-in version instead of the system executable version.
subprocess.run(['command', '-v', 'lualatex'])
on the other hand tries to directly call the system's command
executable, which isn't there, so FileNotFoundError
. The actual sub-process was never spawned at all in this case, since there's no executable to call.
From comments below, it appears that you may need to use shell=True
to use command
on your system. This is generally considered unsafe for multiple reasons. However, you can mitigate some of the risks (e.g. of basic injection) by properly escaping the arguments you send in with shlex.join
. The shlex.join
function converts a list of arguments (first one is command name) to a string that's safe for use with shell=True
; it ensures that the right escaping and quoting is done:
import subprocess, shlex
subprocess.run(shlex.join(['command', '-v', 'lualatex']), shell=True)