Intrepret bytecode with subprocess.call argument - python

I have a program that take one argument.
I need to call this program in my python script and I need to pass the argument in bytecode format (like \x42\x43).
Directly in bash, I can do like this and it does work:
./myprogram $'\x42\x43'
But with subprocess.call it doesn't work:
subprocess.call(["myprogram", "$'\x42\x43'"])
Bytes are not intrepreted.
I try to call my program with /bin/bash but my program returns a segfault!

A quick thing to note: $ is a bash construct. It is the one which evaluates the variable and returns it's value. This does not happen in general when calling one program from another program. So when you invoke myprogram it is up to you to provide all the arguments in a form in which myprogram understands them.

Thanks #acw1668
This works well:
subprocess.call(["myprogram", b"\x42\x43"])
But if yours bytecodes are in a variable, you need to do this manipulation:
# If you're string is unicode type
mystring = mystring.decode('unicode_escape')
# I choose latin1, because some of my bytecode are bigger than 128
mystring = mystring.encode('latin1')
And my bytecodes are correctly interpreted:
subprocess.call(["myprogram", mystring])

Related

how can i execute python commands(?) in python script?

i wonder how to import python console in my python script.
If there are a script named script1.py, that taking string input from console.
What i want to do is that, if i give string input "print(5)" to the script1.py,
than it executes the print(5) (which will give the 5 as result as we use the python console).
Use the python eval() function, as in https://docs.python.org/3/library/functions.html#eval. That evaluates any input string as python code.
For instance:
x = 1
eval('x+1')
2
Be careful with eval though; if the string to be evaluated comes from unprotected user input, it can do anything your own python code can do.

Fail to pass multiple arguments to a python script in command line

I was trying to write a python script like this:
import sys
print sys.argv[1]
print sys.argv[2]
Let's call it arg.py, and run it in command line:
python arg.py one two
it printed: one two.
Everything was fine.
Then I wanted it to be handy so I put arg.py in my $PATH and gave it permission to exacuate so wherever I'm I can simply type arg in command line to run this script. I tried
arg one two
but it failed. The exception said:"bash: test: one: unary operator expected". But if I just do
arg one
it worked fine.
My question is: why I can't pass multiple arguments like this? And what is the right way?
Thanks!
You probably named your script test, which is a Bash builtin name. Name it something else.
$ help test
test: test [expr]
Evaluate conditional expression.
Exits with a status of 0 (true) or 1 (false) depending on
the evaluation of EXPR. Expressions may be unary or binary. Unary
expressions are often used to examine the status of a file. There
are string operators and numeric comparison operators as well.
The behavior of test depends on the number of arguments. Read the
bash manual page for the complete specification.
...
That's why you're getting the error from bash:
bash: test: one: unary operator expected
^--------- because it expects an operator to go before 'two'
^-------- and test doesn't like the argument 'one' you've provided
^-------- because it's interpreting your command as the builtin 'test'
^--- Bash is giving you an error
You should parse command line arguments in Python using argparse or the older optparse.
Your script, as it is, should work. Remember to put a shebang line that tells bash to use Python as interpreter, e.g. #! /usr/bin/env python

%* equivalent in python

Does anybody know what is the equivalent of batch script's %* in python ?
Clarification: in a batch file %* is all parameters specified in the command line -- this is very useful if you want to forward the parameters to another program.
My Problem is that I want to convert one batch call to a python call :
Batch call is :
trial.bat %*
what is the equivalent python call ?(P.S. I know I can just go through the whole sys.argv list and append parameters in some string and pass that to the batch file, but I am looking for a simpler solution here)
I tried following :
os.system('trial.bat '+sys.argv)
os.system('trial.bat '+sys.argv[1:])
But that is not working. I tried similar thing using argparse as well. Its not working either. Please help.
sys.argv[1:] is pretty close. The thing is that argv is a list of arguments, and [1:] is a slice of a list, which is again a list. If you want a string with all arguments combined, you can join them again:
os.system('trial.bat ' + ' '.join(sys.argv[1:]))
Or even better, you use the subprocess module which method’s accept a list of arguments:
subprocess.check_call(['trial.bat'] + sys.argv[1:])
Subprocess is much more flexible when handling parameters and will also behave similar to the parsing in argv. As an example, when calling a script with the arguments foo "hello world" bar, argv will contain this:
>>> sys.argv[1:]
['foo', 'hello world', 'bar']
Now if we were to simply join this list, we would get a single string
>>> ' '.join(sys.argv[1:])
'foo hello world bar'
As you can see, the information of the compound argument hello world is lost, resulting in a completely different meaning.
When using subprocess however, you keep your list and subprocess will automatically make sure to pass these arguments correctly to the called program. So the called program will be able to get hello world as a combined argument too.
You want subprocess.Popen (or one of it's convenience wrappers):
import subprocess
import sys
process = subprocess.Popen(['trial.bat'] + sys.argv[1:])
process.wait()
It's definitely preferred to os.system. The advantage here is that commandline arguments which may need to be quoted to keep their meaning effectively stay quoted. Additionally, this alternative is probably safer than os.system since it avoids creating a subshell.
If you want to use os.system, you need to put the command line back together manually. Python has already parsed the command line apart into separate arguments (or MSVCRT has done it on Python's behalf). This means you need to not just concatenate them back together, but also quote them appropriately.
There is nothing in the stdlib that handles the "quote them appropriately" exactly the way MSVCRT wants. That's partly because Windows quoting is actually ambiguous; there are some cases where it is impossible to round-trip things. But, for simple cases, either POSIX-style quoting (with shlex.quote) or just sticking explicit quotes around each argument will work. So, either of these:
args = ' '.join(shlex.quote(arg) for arg in [program] + sys.argv[1:])
args = ' '.join('"{}"'.format(arg) for arg in [program] + sys.argv[1:])
Then:
os.system(args)
But using subprocess is better than os.system. One reason is that you don't have to fiddle with quoting things; you can just do this:
subprocess.check_call([program] + sys.argv[1:], shell=True)
Someone still needs to put the list of arguments back together in a string so it can be passed to the shell, but now that "someone" is the subprocess module rather than your code.

Calling a subprocess with mixed data type arguments in Python

I am a bit confused as to how to get this done.
What I need to do is call an external command, from within a Python script, that takes as input several arguments, and a file name.
Let's call the executable that I am calling "prog", the input file "file", so the command line (in Bash terminal) looks like this:
$ prog --{arg1} {arg2} < {file}
In the above {arg1} is a string, and {arg2} is an integer.
If I use the following:
#!/usr/bin/python
import subprocess as sbp
sbp.call(["prog","--{arg1}","{arg2}","<","{file}"])
The result is an error output from "prog", where it claims that the input is missing {arg2}
The following produces an interesting error:
#!/usr/bin/python
import subprocess as sbp
sbp.call(["prog","--{arg1} {arg2} < {file}"])
all the spaces seem to have been removed from the second string, and equal sign appended at the very end:
command not found --{arg1}{arg2}<{file}=
None of this behavior seems to make any sense to me, and there isn't much that one can go by from the Python man pages found online. Please note that replacing sbp.call with sbp.Popen does not fix the problem.
The issue is that < {file} isn’t actually an argument to the program, but is syntax for the shell to set up redirection. You can tell Python to use the shell, or you can setup the redirection yourself.
from subprocess import *
# have shell interpret redirection
check_call('wc -l < /etc/hosts', shell=True)
# set up redirection in Python
with open('/etc/hosts', 'r') as f:
check_call(['wc', '-l'], stdin=f.fileno())
The advantage of the first method is that it’s faster and easier to type. There are a lot of disadvantages, though: it’s potentially slower since you’re launching a shell; it’s potentially non-portable because it depends on the operating system shell’s syntax; and it can easily break when there are spaces or other special characters in filenames.
So the second method is preferred.

When using os.execlp, why `python` needs `python` as argv[0]

The code is like this:
os.execlp('python', 'python', 'child.py', #other args#) # this works
os.execlp('python', 'child.py', #other args#) # this doesn't work
I read this question: execlp() in python
But I'm still confused. The answer said:
The first argument is the program to execute (found on the PATH). The
rest are the sys.argv arguments to the program.
However, if I run: python child.py 1 2 3 and the sys.argv of this process would be ["child.py", "1", "2", "3"], where the python doesn't exist. Then why should I add python as the second parameter of os.execlp?
When python is executed, it creates sys.argv for you. The values in that list are based on the arguments passed to it by the operating system, but it leaves off the sys.executable value from that list.
In other words, when Python is invoked, it sets sys.argv to everything but it's own executable.
When you invoke a new executable via os.execlp(), you still need to include Python in that as that is what executable that the OS will run. The first two values of what you a pass to os.execlp() are still required, whatever you find in sys.argv later on.
The second python is a name for python, it can be any string, but it has to be there.
See the second paragraph of http://docs.python.org/3/library/os.html?highlight=os.exec#process-management:
The various exec* functions take a list of arguments for the new program loaded into the process. In each case, the first of these arguments is passed to the new program as its own name rather than as an argument a user may have typed on a command line. For the C programmer, this is the argv[0] passed to a program’s main(). For example, os.execv('/bin/echo', ['foo', 'bar']) will only print bar on standard output; foo will seem to be ignored.
I realize this was answered LONG ago and the answer is basically right, but there are a few things that are misleading in the way it is worded and in the comments to the answer that I would like to address.
First, I think the clearer way to state what is happening is to highlight that the difference is between the Unix argv list that a process gets handed by the OS and the python sys.argv. The python sys.argv is the Unix argv list with the first element (the command name) removed.
The various os.exec* commands use their first argument to be the actual executable to invoke and the remainder of the line is the Unix argv list, which means that the second argument passed to execlp will be interpreted by the executable as the command line name it was invoked as.
Which takes us to the problem with the comment. The reason that the ls example os.execlp('ls','.') "works" is not because ls does anything special to detect it is called with too few arguments. This example code starts the 'ls' executable with the unix argv list being ['.']. That just means that the ls executable gets started while being told (oddly) that it was invoked as '.', and there are no other command line arguments. And what does ls do when it is run with no other command line arguments: it prints the contents of the current directory, or exactly what one mistakenly thought they were doing when the invoked os.execlp('ls', '.').
You can see that this example really isn't "working" by instead trying os.execlp('ls', '/some/non-existant/path'). That also prints out the contents of the current working directory, and would not be mistaken for "working".

Categories

Resources