查找可执行文件时 bash 的奇怪行为
Strange behaviour of bash when finding executable
我正在开发一个 Python 应用程序,它在内部必须调用 ninja
命令,但找不到带有 subprocess
模块的可执行文件。当我传递 shell=True
时,它 会找到 可执行文件,但不会将参数传递给进程。使用 os.system()
有效。
subprocess.call(['ninja'] + args) # Can't find ninja
subprocess.call(['ninja'] + args, shell=True) # Finds & runs ninja, but the additional arguments int "args" are not passed
os.system(' '.join(shlex.quote(x) for x in ['ninja'] + args)) # Works fine
当我检查 ninja
在 bash
中的位置时,我开始觉得有些奇怪了。
$ which ninja
$ type ninja
ninja is hashed (/Users/niklas/Bin/ninja)
$ echo $PATH
~/Bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/texbin:/Library/Frameworks/Python.framework/Versions/3.4/bin/:/Users/niklas/Documents/apache-maven-3.3.3/bin:/Users/niklas/Documents/grib2json-0.8.0-SNAPSHOT/bin
which
找不到 ninja
type ninja
产生它位于 /Users/niklas/Bin/ninja
- 路径
/Users/niklas/Bin
不在 $PATH
我这里的设置出了什么问题?为什么bash实际上可以找到ninja
,而Python只能在shell=True
模式下,然后为什么没有传递额外的参数?
如果您正在设置 shell=True
,那么您的命令必须是字符串而不是列表。考虑:
>>> import subprocess
>>> subprocess.call(['echo', 'hello'], shell=True)
0
>>> subprocess.call('echo hello', shell=True)
hello
0
如果使用 shell=True
传递列表,则仅考虑列表的第一项:
>>> subprocess.call(['echo hello', 'hello'], shell=True)
hello
0
>>>
The path /Users/niklas/Bin is not in $PATH
是的。这是那里的第一项:
$ echo $PATH
~/Bin:/usr/local/bin:...
...假设您是 niklas
。
问题在于 ~
是一种 bash 主义,而 python 调用 os.execvp() 来执行 subprocess.call(),并且这显然是在使用系统调用 execvp(),它 不进行波浪线扩展 .
您的 PATH 应为 /Users/niklas/Bin:...
。当您设置 PATH=~/Bin:$PATH
.
时,波浪号扩展通常由 shell 完成
所以 bash 可以找到它,因为它每次都会重新进行波浪线扩展,但是 execvp() 永远找不到它。
正如@larsks 所说,shell=True
需要一个字符串。
我正在开发一个 Python 应用程序,它在内部必须调用 ninja
命令,但找不到带有 subprocess
模块的可执行文件。当我传递 shell=True
时,它 会找到 可执行文件,但不会将参数传递给进程。使用 os.system()
有效。
subprocess.call(['ninja'] + args) # Can't find ninja
subprocess.call(['ninja'] + args, shell=True) # Finds & runs ninja, but the additional arguments int "args" are not passed
os.system(' '.join(shlex.quote(x) for x in ['ninja'] + args)) # Works fine
当我检查 ninja
在 bash
中的位置时,我开始觉得有些奇怪了。
$ which ninja
$ type ninja
ninja is hashed (/Users/niklas/Bin/ninja)
$ echo $PATH
~/Bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/texbin:/Library/Frameworks/Python.framework/Versions/3.4/bin/:/Users/niklas/Documents/apache-maven-3.3.3/bin:/Users/niklas/Documents/grib2json-0.8.0-SNAPSHOT/bin
which
找不到ninja
type ninja
产生它位于/Users/niklas/Bin/ninja
- 路径
/Users/niklas/Bin
不在$PATH
我这里的设置出了什么问题?为什么bash实际上可以找到ninja
,而Python只能在shell=True
模式下,然后为什么没有传递额外的参数?
如果您正在设置 shell=True
,那么您的命令必须是字符串而不是列表。考虑:
>>> import subprocess
>>> subprocess.call(['echo', 'hello'], shell=True)
0
>>> subprocess.call('echo hello', shell=True)
hello
0
如果使用 shell=True
传递列表,则仅考虑列表的第一项:
>>> subprocess.call(['echo hello', 'hello'], shell=True)
hello
0
>>>
The path /Users/niklas/Bin is not in $PATH
是的。这是那里的第一项:
$ echo $PATH
~/Bin:/usr/local/bin:...
...假设您是 niklas
。
问题在于 ~
是一种 bash 主义,而 python 调用 os.execvp() 来执行 subprocess.call(),并且这显然是在使用系统调用 execvp(),它 不进行波浪线扩展 .
您的 PATH 应为 /Users/niklas/Bin:...
。当您设置 PATH=~/Bin:$PATH
.
所以 bash 可以找到它,因为它每次都会重新进行波浪线扩展,但是 execvp() 永远找不到它。
正如@larsks 所说,shell=True
需要一个字符串。