9

There are two Python interpreters installed:

[user@localhost ~]$ /usr/bin/python -V && /usr/local/bin/python -V
Python 2.4.3
Python 2.7.6

Sudo changes PATH for every command it runs as follows:

[user@localhost ~]$ env | grep PATH && sudo env | grep PATH
PATH=/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/user/bin
PATH=/usr/bin:/bin

I run a test script:

[user@localhost ~]$ cat what_python.py
#!/usr/bin/env python

import sys
print sys.executable
print sys.version
[user@localhost ~]$ sudo python what_python.py
/usr/bin/python
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

and get path to Python 2.4.3 in sys.executable and version 2.7.6 reported in sys.version. Clearly sys.executable and sys.version do not match. Taking into account how sudo modifies PATH I can understand the value of sys.executable. However, why does sys.version report version 2.7.6 and not version 2.4.3, which would match usr/bin/python path reported by sys.executable?

This is a follow-up to my question Sudo changes PATH, yet executes the same binary

4

3 回答 3

6

两个@Graeme

python 可能无法检索到这一事实表明它正在执行自己的 PATH 搜索 (...)

和@twalberg

(...)看起来 sys.executable 搜索当前 PATH 而不是解析 argv[0] (或者可能是因为 argv[0] 在这种情况下是简单的 python ...),(...)

基本上是对的。我不愿意相信 Python 做了一些如此简单(愚蠢?)的事情,比如使用PATH来定位自己,但这是真的。

Python 的sys模块在Python/sysmodule.c文件中实现。从 2.7.6 版开始,sys.executable在第1422行设置如下:

 SET_SYS_FROM_STRING("executable",
                     PyString_FromString(Py_GetProgramFullPath()));

Py_GetProgramFullPath()函数Modules/getpath.c从第701行开始在文件中定义:

char *
Py_GetProgramFullPath(void)
{
    if (!module_search_path)
        calculate_path();
    return progpath;
}

函数calcuate_path()在同一个文件中定义并包含以下注释

/* If there is no slash in the argv0 path, then we have to
 * assume python is on the user's $PATH, since there's no
 * other way to find a directory to start the search from.  If
 * $PATH isn't exported, you lose.
 */

从我的例子中可以看出,当导出的第一个 Python$PATH与正在运行的 Python 不同时,也会失败。

有关计算解释器可执行文件位置的过程的更多信息,请参见文件顶部getpath.c

在完成任何搜索之前,可执行文件的位置已确定。如果 argv[0] 中包含一个或多个斜杠,则其使用不变。否则,它必须是从 shell 的路径调用的,所以我们在 $PATH 中搜索命名的可执行文件并使用它。如果在 $PATH 上找不到可执行文件(或没有 $PATH 环境变量),则使用原始 argv[0] 字符串。

接下来,检查可执行位置以查看它是否是符号链接。如果是这样,则跟踪链接(如果找到,则正确解释相对路径名)并使用链接目标的目录。

让我们做几个测试来验证上述内容:

如果 argv[0] 中包含一个或多个斜杠,则使用原样

[user@localhost ~]$ sudo /usr/local/bin/python what_python.py
/usr/local/bin/python
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

好的。

如果在 $PATH 上找不到可执行文件(或没有 $PATH 环境变量),则使用原始 argv[0] 字符串。

[user@localhost ~]$ sudo PATH= python what_python.py
<empty line>
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

错误的。在这种情况下,来自 sys 模块文档的语句是正确的——如果 Python 无法检索到其可执行文件的真实路径,则 sys.executable 将是一个空字符串或 None。.

让我们看看将 python 的二进制文件的位置添加回PATH(在 sudo 删除它之后)是否可以解决问题:

[user@localhost ~]$ sudo PATH=$PATH python what_python.py
/usr/local/bin/python
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

确实如此。

有关的:

  • Python 问题7774 – sys.executable:如果修改了第零个命令参数,则位置错误。
  • Python 问题10835 – sys.executable 默认和 altinstall
  • python-dev 邮件列表线程——对 sys.executable 进行更严格的定义
  • Stackoverflow问题- 如何在 C 中找到可执行文件的位置
于 2014-03-10T20:55:27.007 回答
1

我认为/usr/local/bin/python是正在运行的可执行文件。版本字符串几乎可以肯定编译成python,所以它不太可能出错。查看文档sys.executable

系统可执行文件

在有意义的系统上,给出 Python 解释器的可执行二进制文件的绝对路径的字符串。如果 Python 无法检索到其可执行文件的真实路径,则 sys.executable 将为空字符串或 None。

可能无法检索到这一点的事实python表明它正在使用 sudo 设置进行自己的PATH搜索PATH(根据我对上一个问题的回答,它与用于查找可执行文件的不同)。

在这里确定的唯一方法是深入研究 python 实现,但通常我会说版本字符串更有可能是您可以信任的字符串。另一方面,sudo用于execve执行命令(至少根据man页面)。您必须指定可执行文件的完整路径execve(一些exec变体自己PATH搜索,而这个没有)。python因此,填写应该是不费吹灰之力sys.executable

我不知道是否有任何方法可以获得解释器的实际名称(argv[0]始终是脚本的名称或),但这会很有趣。如果是,这将是.pythonsys.argv[0]-c/usr/local/bin/pythonpython

我认为最好的办法就是开始secure_path/etc/sudoers希望你会得到一些一致性。

更新

实际上execve接受可执行路径的参数,然后是一个argv数组,所以argv[0]不一定是/usr/local/bin/python. 您仍然可以通过制作如下脚本来找到它:

import time
time.sleep(60)

然后运行它并ps为您提供完整的参数:

sudo python sleep.py &
ps -o args= -C python

还要确定哪个python正在运行,您可以执行以下操作:

sudo ls -l /proc/PID/exe

在程序运行时。

于 2014-03-06T22:28:33.523 回答
0

每次启动 python 解释器时,shell 都会转到 /usr/bin/python 并执行它(尝试下一个:python -c "import os; print(os.environ['_'])")。

那么,正如您所见,ln -l | grep python/usr/bin/python 是指向 python 解释器可执行文件的软链接。

我所做的是:

  1. 安装最新版本的python(去python的网站,下载最后的代码和configure && make && make install
  2. 检查最新版本可执行文件的位置。
  3. 擦除 /usr/bin/python 软链接##(你需要 root 权限 sudo)
  4. ln -s <location最后一个 python 版本的可执行文件> /usr/bin/python ##(很可能需要 sudo)
  5. 从命令行执行 python。
  6. 导入系统

sys.version ## 它应该是最后一个。

于 2015-01-14T19:26:23.633 回答