(问了这个问题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
答案 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