python: obtaining the OSs argv[0], not sys.argv[0]

2024/10/5 23:18:50

(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/shset -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
Answer

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.

https://en.xdnf.cn/q/70428.html

Related Q&A

Why does mypy not accept a list[str] as a list[Optional[str]]?

Example 1: from typing import List, Optionaldef myfunc() -> List[Optional[str]]:some_list = [x for x in "abc"]return some_listMypy complains on example 1:Incompatible return value type (go…

How to do I groupby, count and then plot a bar chart in Pandas?

I have a Pandas dataframe that looks like the following.year month class ---- ----- ----- 2015 1 1 2015 1 1 2015 1 2 2015 1 2 ...I want to be able to create 2 bar chart seri…

How do I execute more code after closing a PyQt window?

Heres an example below:if __name__ == __main__:import sysif (sys.flags.interactive != 1) or not hasattr(QtCore, PYQT_VERSION):QtGui.QApplication.instance().exec_()print "you just closed the pyqt w…

Tor doesnt work with urllib2

I am trying to use tor for anonymous access through privoxy as a proxy using urllib2.System info: Ubuntu 14.04, recently upgraded from 13.10 through dist-upgrade.This is a piece of code I am using for …

Python Selenium Chrome disable prompt for Trying to download multiple files

I am currently running a Python automator which needs to download multiple files within the same session using Selenium Chromedriver.The problem is that when the browser attempts to download the second…

Label outliers in a boxplot - Python

I am analysing extreme weather events. My Dataframe is called df and looks like this:| Date | Qm | |------------|--------------| | 1993-01-…

Matplotlib how to draw vertical line between two Y points

I have 2 y points for each x points. I can draw the plot with this code:import matplotlib.pyplot as pltx = [0, 2, 4, 6] y = [(1, 5), (1, 3), (2, 4), (2, 7)]plt.plot(x, [i for (i,j) in y], rs, markersiz…

Cythonizing fails because of unknown type name uint64_t

This may be a newbie problem. I cant cythonize a simple helloworld.pyx tutorial script while the exact same code works on linux:print("hello world")Here is the setup.py script: from distutils…

How to save changes in read-only Jupyter Notebook

I have opened a python Jupyter notebook but did not notice that it was in read-only, Not Trusted mode. How to save my changes now?Things that I have tried and did not help:File -> Make a Copy File …

How can I invoke an SQLAlchemy query with limit of 1?

I have code like this:thing = thing.query.filter_by(id=thing_id).limit(1).all()[0]all()[0] feels a bit messy and redundant in the limit(1) case. Is there a more terse (and/or otherwise optimal) way to …