【发布时间】:2018-08-31 22:26:49
【问题描述】:
当使用python 显式调用脚本时,argv 被混淆,因此argv[0] 是正在运行的脚本的路径。如果以python foo/bar.py 甚至python -m foo.bar 调用,就会出现这种情况。
我需要一种方法来恢复原始的argv(即python 收到的那个)。不幸的是,这并不像在sys.argv 前添加sys.executable 那样简单,因为python foo/bar.py 与python -m foo.bar 不同(隐含的PYTHONPATH 不同,这取决于您的模块结构可能很重要)。
更具体地说,在python foo/bar.py some other args 和python -m foo.bar some other args 的情况下,我希望分别恢复['python', 'foo/bar.py', 'some', 'other', 'args'] 和['python', '-m', 'foo.bar', 'some', 'other', 'args']。
我知道之前有关于此的问题:
- how to get the ORIGINAL command line in python? with spaces, tabs, etc
- Full command line as it was typed
但这些似乎对 shell 的工作方式有误解,答案反映了这一点。我对撤消 shell 的工作不感兴趣(例如,评估的 shell 变量和函数都很好),我只想得到原始的 argv 给 python。
我发现的only solution是使用/proc/<PID>/cmdline:
import os
with open("/proc/{}/cmdline".format(os.getpid()), 'rb') as f:
original_argv = f.read().split('\0')[:-1]
这确实有效,但它仅适用于 Linux(没有 OSX,而且 Windows 支持似乎需要安装 wmi package)。幸运的是,对于我当前的用例,这个限制很好。但是,如果有一个更干净、跨平台的方法,那就太好了。
/proc/<PID>/cmdline 方法有效的事实让我希望 python 在运行脚本之前不会执行(至少不是系统调用 exec,但可能是 exec 内置)。我记得在某处读到,所有这些参数处理(例如-m)都是在纯 Python 中完成的,而不是 C(这可以通过 python -m this.does.not.exist 产生一个看起来像是来自运行时的异常的事实得到证实)。所以,我敢猜测,在纯 python 中的某个地方,原始的 argv 是可用的(也许这需要通过运行时初始化进行一些探索?)。
tl;dr 是否有一种跨平台(最好是内置的)方法来获取传递给 python 的原始 argv(在它删除 python 可执行文件并转换 @987654358 之前@变成blah.py)?
edit 从 spelunking 中,我发现了 Py_GetArgcArgv,它可以通过 ctypes 访问(找到它 here,链接到 several SO 提到这种方法的帖子):
import ctypes
_argv = ctypes.POINTER(ctypes.c_wchar_p)()
_argc = ctypes.c_int()
ctypes.pythonapi.Py_GetArgcArgv(ctypes.byref(_argc),
ctypes.byref(_argv))
argv = _argv[:_argc.value]
print(argv)
现在这是 OS 可移植的,但不是 python 实现可移植的(仅适用于 cpython,ctypes 如果你不需要它,那就太糟糕了)。另外,特别是,我在 Ubunutu 16.04 上没有得到正确的输出(python -m foo.bar 给了我['python', '-m', '-m']),但我可能只是犯了一个愚蠢的错误(我在 OSX 上得到了相同的行为)。拥有一个完全可移植的解决方案会很棒(不会深入到ctypes)。
【问题讨论】:
-
如何为 python 创建一个包装 C 程序,它将参数存储在一个文件中,并将文件名作为 env 传递。变量到 python 读取? (也称为python,它是一个包装器)。丑陋但可以工作,而且便携。
-
我的意思是:调用包装器“python”并将其放在路径 before 您的原始 python 中(通过在路径中的第二个来从包装器中找到它)。我承认这不是最优的。可以打开 PEP 来请求诸如
sys.original_argv之类的功能。 -
请问获得原始 argv 背后的基本原理是什么?你的最终目标是什么?或许知道这一点,SO 的人或许能够提供帮助。
-
@HaiVu 基本原理是在随机/非确定性测试的背景下(在运行所有测试之前选择了一些随机种子),我想打印出一条关于测试失败的有用错误消息,例如:
Randomized test failed. Run this to reproduce: SEED=123 python -m however.tests.were.run.before。这里的想法是您可以直接复制并粘贴该命令以使用相同的种子重新运行。随机的东西是微不足道的,但我需要能够获得原始的 argv 以产生可以复制和粘贴然后运行的东西。 (rspec 做这样的事情)
标签: python python-3.x command-line-arguments