【问题标题】:What does os.execl do exactly? Why am I getting this error?os.execl 究竟做了什么?为什么我会收到此错误?
【发布时间】:2011-04-30 20:09:22
【问题描述】:

我在使用 virtualenv 在passenger_wsgi 模块上部署Django 时遇到了一些麻烦。 passenger_wsgi.py 文件中的 Python 代码应该可以解决我的问题:

import os, sys
INTERP = '/home/login/.virtualenvs/env_name/bin/python'
if sys.executable != INTERP:
    os.execl(INTERP, INTERP, *sys.argv)

前三行我理解,但我对第四行只有一个非常模糊的想法,而那一行恰好给我一个错误:

/home/login/.virtualenvs/env_name/bin/python: can't find '__main__.py' in ''

那么 os.execl 到底在做什么呢?那条错误消息是什么意思?

【问题讨论】:

  • 你真的有一个名为<login>的目录吗?你是怎么做到的?这很难做到。
  • 不,我只是不想在这里发布我的实际路径;)
  • 加入<login><env_name> 会让你很难尝试你的问题。我不知道你认为你在保护什么“秘密”,但你应该选择没有 shell 特殊字符的名称。
  • 好的,编辑成没有的版本

标签: python virtualenv os.execl


【解决方案1】:

也许你应该这样做:

os.execl(INTERP, *sys.argv) # don't pass again the interpreter path. 

我认为这个文档是错误的:http://wiki.dreamhost.com/Passenger_WSGI

关于执行:

类 Unix 操作系统的 exec 函数是一组函数,这些函数会导致正在运行的进程被作为参数传递给函数的程序完全替换。

os.execl(path, arg0, arg1, ...)
os.execle(path, arg0, arg1, ..., env)
os.execlp(file, arg0, arg1, ...)
os.execlpe(file, arg0, arg1, ..., env)
os.execv(path, args)
os.execve(path, args, env)
os.execvp(file, args)
os.execvpe(file, args, env)

来自: http://docs.python.org/library/os.html

exec*() 函数的“l”和“v”变体在传递命令行参数的方式上有所不同。如果在编写代码时参数的数量是固定的,那么“l”变体可能是最容易使用的;单个参数只是成为 execl*() 函数的附加参数。当参数的数量是可变的时,“v”变体很好,参数在列表或元组中作为 args 参数传递。在任何一种情况下,子进程的参数都应该以正在运行的命令的名称开头,但这不是强制的。

编辑:

我只是做了你在 python shell 中所做的事情,我得到了同样的错误:

>>> import os
>>> import sys
>>> os.execl('/home/login/projects/virtual/bin/python', '/home/login/projects/virtual/bin/python', *sys.argv)
/home/login/projects/virtual/bin/python: can't find '__main__.py' in ''

【讨论】:

  • 谢谢...我读了一些文档,但也不太明白。传递 INTERP 参数一次可以消除错误,但不能消除问题。虽然它确实帮助我理解到底发生了什么;-P 问题是当 INTERP 只传递一次时,python 进程似乎被替换了,但是当我在新进程中打印 sys.executable 时它仍然说'/ usr/bin/python',这显然不是我想要的。
  • 是的,这就是我调试它的方式 - 在 shell 中...... error.log 中没有出现任何错误(显然,乘客很难调试,因为错误记录很糟糕)。而且当passenger_wsgi.py中出现错误时django无法正常工作,我也没有收到任何通过电子邮件发送的错误。
  • 对...我投票赞成您的答案,因为尝试 os.execl(INTERP, *sys.argv) 是我目前拥有的最佳线索。有趣的是,似乎当我这样做时,它实际上并没有将 sys.executable 更改为我在 INTERP 中拥有的任何内容(我假设这是重点?),但总是将其更改为 '/usr/bin /Python'。因此,即使 INTERP = '/usr/bin/python2.7' 而不是 virtualenv 的路径,运行该命令也会使 sys.executable = '/usr/bin/python'
  • sys.executable不会改变是正常的,你可以尝试运行一个虚拟环境的python解释器,你会看到sys.executable == '/usr/bin/python',那是因为sys.executable 不会给你正在运行的解释器,而是给你系统默认解释器。
  • 我认为 wiki 页面实际上并没有错。如果您检查python documentation here,它会说“这些参数中的第一个作为它自己的名称传递给新程序”,因此两次传递 INTERP 路径是正确的:一次调用该解释器,再次告诉它自己的位置.
【解决方案2】:

我无意搞砸一个 9 岁的问题,我很快在 Google 上搜索了“Python execl 示例”并碰到了这个帖子,几乎被答案误导了,所以我发帖希望能帮助其他访问者。

我同意https://stackoverflow.com/users/479633/mouad关于重现错误的方式,但不是原因,错误发生是因为当交互式打开python解释器时,sys.argv将是[''],所以一个空字符串传递给execl调用的python解释器作为主脚本(目录)的路径,由于在目录''(当前工作目录)中找不到主脚本文件__main__.py,它抱怨:

can't find '__main__.py' in ''

我无法弄清楚https://stackoverflow.com/users/211075/monika-sulik 是如何在成功将sys.argv 的第一个成员设置为'' 的同时成功运行python 脚本的,我纯粹是猜测代码被复制粘贴到了REPL。

正如Python: os.execl() - what does it do exactly? Why am I getting this error?中提到的https://stackoverflow.com/users/845210/bjmc,文档是正确的,通过两次解释器路径是可以的,尽管第二次不需要。该函数的签名根植于 UNIX execve() API (https://linux.die.net/man/2/execve),它表示:

argv 是传递给新程序的参数字符串数组。经过 约定,这些字符串中的第一个应包含文件名 与正在执行的文件相关联。

有些程序利用了这种不一致,例如忙箱。

$ ln -s /bin/busybox cat
$ ./cat /etc/timezone
/UTC
$ python -c "import os; os.execl('./cat', 'cat', '/etc/timezone')"
/UTC
$ python -c "import os; os.execl('./cat', 'ls', '/etc/timezone')"
/etc/timezone

可执行文件路径与main() 中的argv[0] 之间的不一致使得在类UNIX 环境中获取运行python 可执行文件的可靠路径非常困难(如果不是不可能的话),这里有一个脚本来说明这一点:

import os
import sys


if len(sys.argv) >= 2 and sys.argv[1] == 'exec':
    os.execl('/usr/bin/python', 'ls', sys.argv[0])
else:
    print(sys.executable)
    print(sys.version)
    print(sys.argv)

运行这个脚本

$ python test.py exec
/bin/ls
2.7.13 (default, Nov 24 2017, 17:33:09)
[GCC 6.3.0 20170516]
['test.py']

正如文档 (https://docs.python.org/3/library/sys.html#sys.executable) 所说,sys.executable 的价值为 "/bin/ls"

一个字符串,给出可执行二进制文件的绝对路径 Python 解释器,在有意义的系统上

关于sys.executable,如果python 开发人员无法弄清楚如何让sys.executable 指向正在运行的python 可执行文件的路径,那么在类UNIX 环境中可能没有意义。如果有人告诉我其他情况,我将不胜感激。

【讨论】:

    【解决方案3】:
    >>> import os
    >>> help(os.execl)
    
    
    execl(file, *args)
        execl(file, *args)
    
        Execute the executable file with argument list args, replacing the
        current process.
    

    这可能有助于解决您的问题:http://ubuntuforums.org/showthread.php?t=1493979

    【讨论】:

    • 谢谢,但我还是不明白... 为什么我必须将 INTERP 的值作为文件名和参数之一输入? “替换当前流程”究竟是什么意思?那错误信息呢,是什么意思?
    • @Monika Sulik :替换当前进程意味着 exec* 命令不会杀死并创建新进程,它们只是替换调用命令的进程,进程 ID 不会t 改变,但调用进程的数据、堆和堆栈被新进程的数据、堆和堆栈替换。
    • @singularity - 谢谢,这确实至少让我明白了过程的事情:)
    猜你喜欢
    • 2013-08-06
    • 1970-01-01
    • 2021-03-05
    • 2014-06-15
    • 1970-01-01
    • 2016-07-18
    • 1970-01-01
    • 2019-12-23
    • 1970-01-01
    相关资源
    最近更新 更多