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

Bil*_*osa 5 python

(这个问题是在这里问的,但答案是特定于 Linux 的;我在 FreeBSD 和 NetBSD 系统上运行,这些系统(编辑:通常)没有/proc。)

Python 似乎很笨拙argv[0],因此您不会像 C 程序那样获得传递给进程的内容。公平地说,sh、bash 和 Perl 也好不到哪里去。有什么办法可以解决这个问题,这样我的 Python 程序就可以获得原始值吗?我在这个 FreeBSD 系统上有管理权限,可以做一些事情,比如更改每个人的默认 PATH 环境变量以指向包含 python2 和 python3 的目录之前的某个其他目录,但我无法控制创建/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
Run Code Online (Sandbox Code Playgroud)

...现在脚本:

#!/bin/sh

set -e
rm -rf work
mkdir work
cd work
cat > childc.c << EOD; cc childc.c -o childc
#include <stdio.h>
int main(int    argc,
         char **argv
        )
{
  printf("the C child program gets it right: ");
  printf("%s %s\n",argv[0],argv[1]);
}
EOD
cat > something2.py <<EOD; chmod 700 something2.py
#!/usr/bin/env python2
import sys
print "the python2 program dumbs it down:", sys.argv
EOD
cat > something3.py <<EOD; chmod 700 something3.py
#!/usr/bin/env python3
import sys
print("the python3 program dumbs it down:", sys.argv)
EOD
cat > shscript.sh <<EOD; chmod 700 shscript.sh
#!/bin/sh
echo "the sh script       dumbs it down:" \$0 \$1
EOD
cat > bashscript.sh <<EOD; chmod 700 bashscript.sh
#!/bin/sh
echo "the bash script     dumbs it down:" \$0 \$1
EOD
cat > something.pl <<EOD; chmod 700 something.pl
#!/usr/bin/env perl
print("the perl script drops arg0:        \$0 \$ARGV[0]\n")
EOD
cat > launch.c << EOD; cc launch.c -o launch; launch
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int    argc,
         char **argv,
         char **arge)
{
  int    child_status;
  size_t program_index;
  pid_t  child_pid;

  char  *program_list[]={"./childc",
                         "./something2.py",
                         "./something3.py",
                         "./shscript.sh",
                         "./bashscript.sh",
                         "./something.pl",
                         NULL
                        };

  char  *some_args[]={"arbitrary-arg0","arbitrary-arg1",NULL};

  for(program_index=0;
      program_list[program_index];
      program_index++
     )
  {
    child_pid=fork();

    if(child_pid<0)
    {
      perror("fork()");
      exit(1);
    }
    if(child_pid==0)
    {
      execve(program_list[program_index],some_args,arge);
      perror("execve");
      exit(1);
    }
    wait(&child_status);
  }

  return 0;
}
EOD
Run Code Online (Sandbox Code Playgroud)

kab*_*nus 1

我认为这里阻力最小的路径有点hacky,但可能适用于任何操作系统。基本上你双重包装了你的 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);
}
Run Code Online (Sandbox Code Playgroud)

你可以让它更快,但是嘿,过早优化?

请注意,我们正在调用一个名为wrap_python.py(可能您需要此处的完整路径)的脚本。我们想要传递 "true" argv,但我们需要在 Python 上下文中进行一些操作以使其透明。trueargv[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.
Run Code Online (Sandbox Code Playgroud)

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

这将运行为

python3 my_python_script arg1 arg2 etc...
Run Code Online (Sandbox Code Playgroud)

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

import sys
print(__name__)
print("Got", sys.argv)
Run Code Online (Sandbox Code Playgroud)

产量

__main__
Got ['./wrap_python', 'test.py', 'hello', 'world', 'this', '1', '2', 'sad']
Run Code Online (Sandbox Code Playgroud)

注意我调用了我的程序wrap_python- 你想命名它python3