For example, I can use Python scripts in PHP like there:
exec("python script.py params",$result);
where "script.py" - script name and variable $result save output data.
How I can make it with Ruby? I mean, call Python scripts from Ruby.
You can shell-out to any shell binary and capture the response with backticks:
result = `python script.py params`
One way would be exec.
result = exec("python script.py params")
Another way to do the same thing would be,
system 'python script.py', params1, params2
I used the following python call from a ruby module.
In my case using exec() didn't work because it interrupts the current process and caused the simulation I was running to fail.
# run python script
`python #{py_path_py} #{html_path_py} #{write_path_py}`
You can use backticks to accomplish this in two ways
result = `python script.py params`
or
result = %(python script.py params)
Related
I've come across a situation where it would be convenient to use python within a bash script I'm writing. I call some executables within my script, then want to do a bit of light data processing with python, then carry on. It doesn't seem worth it to me to write a dedicated script for the processing.
So what I want to do is something like the following:
# do some stuff in bash script
# write some data into datafile.d
python_fragment= << EOF
f = open("datafile.d")
// do some stuff with opened file
print(result)
EOF
result=$(execute_python_fragment $python_fragment) # <- what I want to do
# do some stuff with result
Basically all I want to do is execute a string containing python code. I could of course just make another file containing the python code and execute that, but I'd prefer not to do so. I could do something like echo $python_fragment > temp_code_file, then execute temp_code_file, but that seems inelegant. I just want to execute the string directly, if that's possible.
What I want to do seems simple enough, but haven't figured it out or found the solution online.
Thanks!
You can run a python command direct from the command line with -c option
python -c 'from foo import hello; print (hello())'
Then with bash you could do something like
result=$(python -c '$python_fragment')
You only have to redirect that here-string/document to python
python <<< "print('Hello')"
or
python <<EOF
print('Hello')
EOF
and encapsulate that in a function
execute_python_fragment() {
python <<< "$1"
}
and now you can do your
result=$(execute_python_fragment "${python_fragment}")
You should also add some kind of error control, input sanitizing... it's up to you the level of security you need in this function.
If the string contains the exact python code, then this simple eval() function works.
Here's a really basic example:
>>> eval("print(2)")
2
Hope that helps.
maybe something like
result=$(echo $python_fragment | python3)
only problem is the heredoc assignment in the question doesn't work either. But https://stackoverflow.com/a/1167849 suggests a way to do it if that is what you want to do:
python_fragment=$(cat <<EOF
print('test message')
EOF
) ;
result=$(echo $python_fragment | python3)
echo result was $result
I am writing a bash script in which a small python script is embedded. I want to pass a variable from python to bash. After a few search I only found method based on os.environ.
I just cannot make it work. Here is my simple test.
#!/bin/bash
export myvar='first'
python - <<EOF
import os
os.environ["myvar"] = "second"
EOF
echo $myvar
I expected it to output second, however it still outputs first. What is wrong with my script? Also is there any way to pass variable without export?
summary
Thanks for all answers. Here is my summary.
A python script embedded inside bash will run as child process which by definition is not able to affect parent bash environment.
The solution is to pass assignment strings out from python and eval it subsequently in bash.
An example is
#!/bin/bash
a=0
b=0
assignment_string=$(python -<<EOF
var1=1
var2=2
print('a={};b={}'.format(var1,var2))
EOF
)
eval $assignment_string
echo $a
echo $b
Unless Python is used to do some kind of operation on the original data, there's no need to import anything. The answer could be as lame as:
myvar=$(python - <<< "print 'second'") ; echo "$myvar"
Suppose for some reason Python is needed to spit out a bunch of bash variables and assignments, or (cautiously) compose code on-the-fly. An eval method:
myvar=first
eval "$(python - <<< "print('myvar=second')" )"
echo "$myvar"
Complementing the useful Cyrus's comment in question, you just can't do it. Here is why,
Setting an environment variable sets it only for the current process and any child processes it launches. os.environ will set it only for the shell that is running to execute the command you provided. When that command finishes, the shell goes away, and so does the environment variable.
You can pretty much do that with a shell script itself and just source it to reflect it on the current shell.
There are a few "dirty" ways of getting something like this done. Here is an example:
#!/bin/bash
myvar=$(python - <<EOF
print "second"
EOF
)
echo "$myvar"
The output of the python process is stored in a bash variable. It gets a bit messy if you want to return more complex stuff, though.
You can make python return val and pass it to bash:
pfile.py
print(100)
bfile.sh
var=$(python pfile.py)
echo "$var"
output: 100
Well, this may not be what you want but one option could be running the other batch commands in python using subprocess
import subprocess
x =400
subprocess.call(["echo", str(x)])
But this is more of a temporary work around. The other solutions are more along what you are looking for.
Hope I was able to help!
I need to write a code in Python using functions that a friend of mine developed in shell. Is that possible? Can I do something like
output = subprocess.call('friends_developed_function', shell = True)
You need to make sure your friend's function is defined before you can call it. You cannot call a function which was defined in a parent process [Note 1]. So you could execute the following:
output = subprocess.check_output(
'source /path/to/function/definition; the_function args if needed',
shell = True)
Note that I changed subprocess.call to subprocess.check_output so that the call will return the output of the shell function, instead of its exit code.
It's a little awkward fixing the path to the script file with the function definitions. You could instead just define the function directly before calling it, using a string literal:
output = subprocess.check_output(
"""my_func() { echo The argument is "$1"; }
my_func the_argument
""",
shell = True)
Notes:
Unless you are using bash, but that probably won't work for os.system or subprocess.call(..., shell=True) because those will use the basic shell /bin/sh, which often is not bash. Even if you forced the use of bash, and you had properly exported the function definitions, it would still be a bad idea because your python script would only work if the environment were set up correctly.
There is a couple of ways to do this, I'm posting what I am familiar with.
with open(r"*file location", 'wb', 0) as file:
subprocess.check_call(*command*, stdout=file)
Now the output is in the text file location.I used check_call to validate the command so I assume subprocess.call() just executes the command.
I have two scripts, the main is in Python 3, and the second one is written in Python 2 (it also uses a Python 2 library).
There is one method in the Python 2 script I want to call from the Python 3 script, but I don't know how to cross this bridge.
Calling different python versions from each other can be done very elegantly using execnet. The following function does the charm:
import execnet
def call_python_version(Version, Module, Function, ArgumentList):
gw = execnet.makegateway("popen//python=python%s" % Version)
channel = gw.remote_exec("""
from %s import %s as the_function
channel.send(the_function(*channel.receive()))
""" % (Module, Function))
channel.send(ArgumentList)
return channel.receive()
Example: A my_module.py written in Python 2.7:
def my_function(X, Y):
return "Hello %s %s!" % (X, Y)
Then the following function calls
result = call_python_version("2.7", "my_module", "my_function",
["Mr", "Bear"])
print(result)
result = call_python_version("2.7", "my_module", "my_function",
["Mrs", "Wolf"])
print(result)
result in
Hello Mr Bear!
Hello Mrs Wolf!
What happened is that a 'gateway' was instantiated waiting
for an argument list with channel.receive(). Once it came in, it as been translated and passed to my_function. my_function returned the string it generated and channel.send(...) sent the string back. On other side of the gateway channel.receive() catches that result and returns it to the caller. The caller finally prints the string as produced by my_function in the python 3 module.
You could run python2 using subprocess (python module) doing the following:
From python 3:
#!/usr/bin/env python3
import subprocess
python3_command = "py2file.py arg1 arg2" # launch your python2 script
process = subprocess.Popen(python3_command.split(), stdout=subprocess.PIPE)
output, error = process.communicate() # receive output from the python2 script
Where output stores whatever python 2 returned
Maybe to late, but there is one more simple option for call python2.7 scripts:
script = ["python2.7", "script.py", "arg1"]
process = subprocess.Popen(" ".join(script),
shell=True,
env={"PYTHONPATH": "."})
I am running my python code with python 3, but I need a tool (ocropus) that is written with python 2.7. I spent a long time trying all these options with subprocess, and kept having errors, and the script would not complete. From the command line, it runs just fine. So I finally tried something simple that worked, but that I had not found in my searches online. I put the ocropus command inside a bash script:
#!/bin/bash
/usr/local/bin/ocropus-gpageseg $1
I call the bash script with subprocess.
command = [ocropus_gpageseg_path, current_path]
process = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
output, error = process.communicate()
print('output',output,'error',error)
This really gives the ocropus script its own little world, which it seems to need. I am posting this in the hope that it will save someone else some time.
It works for me if I call the python 2 executable directly from a python 3 environment.
python2_command = 'C:\Python27\python.exe python2_script.py arg1'
process = subprocess.Popen(python2_command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
python3_command = 'python python3_script.py arg1'
process = subprocess.Popen(python3_command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
I ended up creating a new function in the python3 script, which wraps the python2.7 code. It correctly formats error messages created by the python2.7 code and is extending mikelsr's answer and using run() as recommended by subprocess docs.
in bar.py (python2.7 code):
def foo27(input):
return input * 2
in your python3 file:
import ast
import subprocess
def foo3(parameter):
try:
return ast.literal_eval(subprocess.run(
[
"C:/path/to/python2.7/python.exe", "-c", # run python2.7 in command mode
"from bar import foo27;"+
"print(foo27({}))".format(parameter) # print the output
],
capture_output=True,
check=True
).stdout.decode("utf-8")) # evaluate the printed output
except subprocess.CalledProcessError as e:
print(e.stdout)
raise Exception("foo27 errored with message below:\n\n{}"
.format(e.stderr.decode("utf-8")))
print(foo3(21))
# 42
This works when passing in simple python objects, like dicts, as the parameter but does not work for objects created by classes, eg. numpy arrays. These have to be serialized and re-instantiated on the other side of the barrier.
Note: This was happening when running my python 2.x s/w in the liclipse IDE.
When I ran it from a bash script on the command line it didn't have the problem.
Here is a problem & solution I had when mixing python 2.x & 3.x scripts.
I am running a python 2.6 process & needed to call/execute a python 3.6 script.
The environment variable PYTHONPATH was set to point to 2.6 python s/w, so it was choking on the followng:
File "/usr/lib64/python2.6/encodings/__init__.py", line 123
raise CodecRegistryError,\
This caused the 3.6 python script to fail.
So instead of calling the 3.6 program directly I created a bash script which nuked the PYTHONPATH environment variable.
#!/bin/bash
export PYTHONPATH=
## Now call the 3.6 python scrtipt
./36psrc/rpiapi/RPiAPI.py $1
Instead of calling them in python 3, you could run them in conda env batch by creating a batch file as below:
call C:\ProgramData\AnacondaNew\Scripts\activate.bat
C:\Python27\python.exe "script27.py"
C:\ProgramData\AnacondaNew\python.exe "script3.py"
call conda deactivate
pause
I recommend to convert the Python2 files to Python3:
https://pythonconverter.com/
I know that I can run a python script from my bash script using the following:
python python_script.py
But what about if I wanted to pass a variable / argument to my python script from my bash script. How can I do that?
Basically bash will work out a filename and then python will upload it, but I need to send the filename from bash to python when I call it.
To execute a python script in a bash script you need to call the same command that you would within a terminal. For instance
> python python_script.py var1 var2
To access these variables within python you will need
import sys
print(sys.argv[0]) # prints python_script.py
print(sys.argv[1]) # prints var1
print(sys.argv[2]) # prints var2
Beside sys.argv, also take a look at the argparse module, which helps define options and arguments for scripts.
The argparse module makes it easy to write user-friendly command-line interfaces.
Use
python python_script.py filename
and in your Python script
import sys
print sys.argv[1]
Embedded option:
Wrap python code in a bash function.
#!/bin/bash
function current_datetime {
python - <<END
import datetime
print datetime.datetime.now()
END
}
# Call it
current_datetime
# Call it and capture the output
DT=$(current_datetime)
echo Current date and time: $DT
Use environment variables, to pass data into to your embedded python script.
#!/bin/bash
function line {
PYTHON_ARG="$1" python - <<END
import os
line_len = int(os.environ['PYTHON_ARG'])
print '-' * line_len
END
}
# Do it one way
line 80
# Do it another way
echo $(line 80)
http://bhfsteve.blogspot.se/2014/07/embedding-python-in-bash-scripts.html
use in the script:
echo $(python python_script.py arg1 arg2) > /dev/null
or
python python_script.py "string arg" > /dev/null
The script will be executed without output.
I have a bash script that calls a small python routine to display a message window. As I need to use killall to stop the python script I can't use the above method as it would then mean running killall python which could take out other python programmes so I use
pythonprog.py "$argument" & # The & returns control straight to the bash script so must be outside the backticks. The preview of this message is showing it without "`" either side of the command for some reason.
As long as the python script will run from the cli by name rather than python pythonprog.py this works within the script. If you need more than one argument just use a space between each one within the quotes.
and take a look at the getopt module.
It works quite good for me!
Print all args without the filename:
for i in range(1, len(sys.argv)):
print(sys.argv[i])