为什么在 subprocess.run() 中调用 `command -v` 会引发 FileNotFoundError?
Why does calling `command -v` in subprocess.run() raise a FileNotFoundError?
如果我从 /bin/bash
或 /bin/sh
运行 command
,我得到类似的结果:
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码每次都是0).
如果我从 python 运行,看起来我需要使用 shell=True
,虽然我不明白为什么:
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'
最令人惊讶的是它引发了 FileNotFoundError
,而不是 subprocess.CalledProcessError
。
添加 text=True
和 check=True
之一或两者都不会改变任何东西。
像 command
一样使用其他东西确实可以按预期工作(CompletedProcess
和 0
的 returncode
或 subprocess.CalledProcessError
)。我试过 printf
、echo
、ls
、cat
、which
...
那么,command
有什么特别之处?
command
是 bash
和其他 shell 的 built-in 关键字。很可能,您的系统在其搜索 PATH
.
中没有实际的 /usr/bin/command
可执行文件
subprocess.run('command -v lualatex', shell=True)
大致等同于默认为 bash
shell 的系统上的 subprocess.run(['bash', '-c', 'command -v lualatex'])
。然后 bash
会将 command
解释为它的 built-in 版本而不是系统可执行版本。
另一方面,subprocess.run(['command', '-v', 'lualatex'])
试图直接调用系统的 command
可执行文件,但它不存在,因此 FileNotFoundError
。在这种情况下,实际的 sub-process 根本没有生成,因为没有可执行文件可以调用。
从下面的评论来看,您可能需要使用 shell=True
才能在您的系统上使用 command
。由于多种原因,这通常被认为是不安全的。但是,您可以通过适当地转义使用 shlex.join
发送的参数来减轻 一些 的风险(例如基本注入)。 shlex.join
函数将参数列表(第一个是命令名称)转换为可安全用于 shell=True
的字符串;它确保完成正确的转义和引用:
import subprocess, shlex
subprocess.run(shlex.join(['command', '-v', 'lualatex']), shell=True)
如果我从 /bin/bash
或 /bin/sh
运行 command
,我得到类似的结果:
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码每次都是0).
如果我从 python 运行,看起来我需要使用 shell=True
,虽然我不明白为什么:
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'
最令人惊讶的是它引发了 FileNotFoundError
,而不是 subprocess.CalledProcessError
。
添加 text=True
和 check=True
之一或两者都不会改变任何东西。
像 command
一样使用其他东西确实可以按预期工作(CompletedProcess
和 0
的 returncode
或 subprocess.CalledProcessError
)。我试过 printf
、echo
、ls
、cat
、which
...
那么,command
有什么特别之处?
command
是 bash
和其他 shell 的 built-in 关键字。很可能,您的系统在其搜索 PATH
.
/usr/bin/command
可执行文件
subprocess.run('command -v lualatex', shell=True)
大致等同于默认为 bash
shell 的系统上的 subprocess.run(['bash', '-c', 'command -v lualatex'])
。然后 bash
会将 command
解释为它的 built-in 版本而不是系统可执行版本。
subprocess.run(['command', '-v', 'lualatex'])
试图直接调用系统的 command
可执行文件,但它不存在,因此 FileNotFoundError
。在这种情况下,实际的 sub-process 根本没有生成,因为没有可执行文件可以调用。
从下面的评论来看,您可能需要使用 shell=True
才能在您的系统上使用 command
。由于多种原因,这通常被认为是不安全的。但是,您可以通过适当地转义使用 shlex.join
发送的参数来减轻 一些 的风险(例如基本注入)。 shlex.join
函数将参数列表(第一个是命令名称)转换为可安全用于 shell=True
的字符串;它确保完成正确的转义和引用:
import subprocess, shlex
subprocess.run(shlex.join(['command', '-v', 'lualatex']), shell=True)