Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python: obtaining the OS's argv[0], not sys.argv[0]

Tags:

python

(This question was asked here, but the answer was Linux-specific; I'm running on FreeBSD and NetBSD systems which (EDIT: ordinarily) do not have /proc.)

Python seems to dumb down argv[0], so you don't get what was passed in to the process, as a C program would. To be fair, sh and bash and Perl are no better. Is there any way I can work around this, so my Python programs can get that original value? I have administrative privileges on this FreeBSD system, and can do things like changing everyone's default PATH environment variable to point to some other directory before the one that contains python2 and python3, but I don't have control over creating /proc. I have a script which illustrates the problem. First, the script's output:

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

... and now the script:

#!/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
like image 320
Bill Evans at Mariposa Avatar asked Dec 12 '19 04:12

Bill Evans at Mariposa


People also ask

How to parse arguments in the Python argv method?

The arguments in the Python argv method are always parsed as the String. You must be careful if you expect your input to be of any other data type. You may need to cast or convert the items according to your requirements. You can store any number of arguments given at the start of the program in the variables. Let’s see the following example.

How to get Python command line arguments from a file?

sys.argv [] is used to get python command line arguments. sys.argv [0] ‘s value is the file path of the python source code file that is running. For example, if you run python test_sys_arg.py in a terminal, then sys.argv [0] ‘s value is test_sys_arg.py.

How do I pass arguments to a Python program?

We can use sys.argv to pass arguments to Python programs from the command line, or terminal. In our example, the sys module was used to pass the names of files to our program, but you’ll find there are a variety of ways to use this tool.

What is the use of Sys in Python?

To employ this module named “ sys ” is used. sys.argv is similar to an array and the values are also retrieved like Python array. The sys module provides functions and variables used to manipulate different parts of the Python runtime environment.


1 Answers

What follows is a generally useful answer to what I meant to ask.

The answer that kabanus gave is excellent, given the way I phrased the problem, so of course he gets the up-arrow and the checkmark. The transparency is a beautiful plus, in my opinion.

But it turns out that I didn't specify the situation completely. Each python script starts with a shebang, and the shebang feature makes it more complicated to launch a python script with an artificial argv[0].

Also, transparency isn't my goal; backward compatibility is. I would like the normal situation to be that sys.argv works as shipped, right out of the box, without my modifications. Also, I would like any program which launches a python script with an artificial argv[0] not to have to worry about any additional argument manipulation.

Part of the problem is to overcome the "shebang changing argv" problem.

The answer is to write a wrapper in C for each script, and the launching program launches that program instead of the actual script. The actual script looks at the arguments to the parent process (the wrapper).

The cool thing is that this can work for script types other than python. You can download a proof of concept here which demonstrates the solution for python2, python3, sh, bash, and perl. You'll have to change each CRLF to LF, using dos2unix or fromdos. This is how the python3 script handles it:

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]

The solution does not rely on /proc, so it works on FreeBSD as well as Linux.

like image 182
Bill Evans at Mariposa Avatar answered Oct 29 '22 05:10

Bill Evans at Mariposa