python:获取操作系统的argv [0],而不是sys.argv [0]

时间:2019-12-12 04:24:16

标签: python

(问了这个问题here,但是答案是Linux特定的;我正在FreeBSD和NetBSD系统上运行,这些系统(通常 EDIT :通常)没有{{1 }}。

Python似乎简化了/proc,因此您不会像C程序那样获得传递给进程的内容。公平地说,sh,bash和Perl并不更好。有什么办法可以解决此问题,以便我的Python程序可以获得该原始值?我在这个FreeBSD系统上拥有管理特权,并且可以做一些事情,例如更改每个人的默认PATH环境变量,使其指向包含python2和python3的目录之前的其他目录,但是我无法控制创建argv[0]。我有一个说明问题的脚本。首先,脚本的输出:

/proc

...,现在是脚本:

the C child program gets it right: arbitrary-arg0 arbitrary-arg1
the python2 program dumbs it down: ['./something2.py', 'arbitrary-arg1']
the python3 program dumbs it down: ['./something3.py', 'arbitrary-arg1']
the sh script       dumbs it down: ./shscript.sh arbitrary-arg1
the bash script     dumbs it down: ./bashscript.sh arbitrary-arg1
the perl script drops arg0:        ./something.pl arbitrary-arg1

3 个答案:

答案 0 :(得分:2)

以下是我问的一般有用的答案。

考虑到我对问题的表述方式, kabanus 给出的答案非常好,因此,他当然得到了向上箭头和对勾。我认为透明性是一个美丽的优点。

但是事实证明我并没有完全说明情况。每个python脚本都以shebang开头,而shebang功能使启动带有人工argv [0]的python脚本变得更加复杂。

此外,透明度不是我的目标;向后兼容是。我希望通常的情况是sys.argv可以在出厂时直接使用,而无需修改。另外,我希望任何使用人工argv [0]启动python脚本的程序都不必担心任何其他参数操作。

部分问题是要克服“ shebang change argv”问题。

答案是用C为每个脚本编写一个包装,然后启动程序将启动该程序,而不是实际的脚本。实际的脚本查看父进程(包装器)的参数。

很棒的事情是,它可以用于python以外的脚本类型。您可以下载概念证明here,它演示了python2,python3,sh,bash和perl的解决方案。您必须使用dos2unix或fromdos将每个CRLF更改为LF。 python3脚本是这样处理的:

def get_arg0():
    return subprocess.run("ps -p %s -o 'args='" % os.getppid(),
                          shell=True,
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE
                         ).stdout.decode(encoding='latin1').split(sep=" ")[0]

该解决方案不依赖/ proc,因此可以在FreeBSD和Linux上使用。

答案 1 :(得分:1)

我认为这里阻力最小的路径有些古怪,但是可能适用于任何操作系统。基本上,您会双重包装Python调用。首先(以Python 3为例),您的路径中的Python3被一个小的C程序所取代,您知道它可以信任:

#include<stdlib.h>
#include<string.h>
int main(int argc, char **argv) {
    // The python 3 below should be replaced by the path to the original one
    // In my tests I named this program wrap_python so there was no problem
    // but if you are changing this system wide (and calling the wrapper python3
    //  you can't leave this.
    const char *const program = "python3 wrap_python.py";
    size_t size = strlen(program) + 1; // Already added null character at end
    for(int count = 0; count < argc; ++count)
        size += strlen(argv[count]) + 1; // + 1 for space

    char *cmd = malloc(size);
    if(!cmd) exit(-1);
    cmd[0] = '\0';
    strcat(cmd, program);
    for(int count = 1; count < argc; ++count) {
        strcat(cmd, " ");
        strcat(cmd, argv[count]);
    }
    strcat(cmd, " ");
    strcat(cmd, argv[0]);
    return system(cmd);
}

您可以使速度更快,但是,过早优化了吗?

请注意,我们正在调用一个名为wrap_python.py的脚本(可能在这里需要完整的路径)。我们想传递“ true” argv,但是我们需要在Python上下文中进行一些操作以使其透明。真实的argv[0]作为最后一个参数传递,而wrap_python.py是:

from sys import argv
argv[0] = argv.pop(-1)
print("Passing:", argv) # Delete me
exit(exec(open(argv[1]).read())) # Different in Python 2. Close the file handle if you're pedantic.

我们的小型包装器将argv[0]替换为我们的C包装器提供的包装器,将其从末尾删除,然后在相同的上下文中手动执行。特别是__name__ == __main__是正确的。

这将作为

python3 my_python_script arg1 arg2 etc...

您的路径现在将指向该原始C程序。对此进行测试

import sys
print(__name__)
print("Got", sys.argv)

收益

__main__
Got ['./wrap_python', 'test.py', 'hello', 'world', 'this', '1', '2', 'sad']

请注意,我调用了程序wrap_python-您想将其命名为python3

答案 2 :(得分:0)

使用Python的ctypes模块获取“程序名称”,默认情况下将其设置为argv [0]。 See Python source code here。例如:

import ctypes

GetProgramName = ctypes.pythonapi.Py_GetProgramName
GetProgramName.restype = ctypes.c_wchar_p

def main():
    print(GetProgramName())

if __name__ == '__main__':
    main()

运行命令将打印:

$ exec -a hello python3 name.py 
hello