I am executing a python script from bash script as follows
"python -O myscript.pyo &"
After launching the python script I need to press "enter" manually to get prompt.
Is there a way to avoid this manual intervention.
Thanks in advance!
pipe a blank input to it:
echo "" | python -O myscript.pyo
you might want to create a bash alias to save keyhits: alias run_myscript="echo '' | python -O myscript.pyo"
placing wait after the line to run the process in background seems to work.
Source:
http://www.iitk.ac.in/LDP/LDP/abs/html/abs-guide.html#WAITHANG
Example given:
!/bin/bash
test.sh
ls -l &
echo "Done."
wait
Many thanks
A very convenient way to execute a Python script on a remote server is to pipe it to ssh:
cat script.py | ssh user#address.com python -
where the - seems to be optional.
How can I execute other bash commands before running the Python script in this way?
This does not work:
cat script.py | ssh user#address.com "cd ..; python -" # WRONG!
Interestingly, this sends a non-deterministically corrupted version of the Python script, which gives a syntax error in a different place every time you run it!
You can create a sub-shell:
cat script.py | ssh user#address.com "(cd ..; python -)"
Or a temporary file:
cat script.py | ssh user#address.com "tee >/tmp/tmp.py; cd ..; python /tmp/tmp.py; rm /tmp/tmp.py"
I am building an interactive installer using a nifty command line:
curl -L http://install.example.com | bash
The bash script then rapidly delegates to a python script:
# file: install.sh
[...]
echo "-=- Welcome -=-"
[...]
/usr/bin/env python3 deploy_p3k.py
And the python script itself prompts the user for input:
# file: deploy_py3k.py
[...]
input('====> Confirm or enter installation directory [/srv/vhosts/project]: ')
[...]
input('====> Confirm installation [y/n]: ')
[...]
PROBLEM: Because the python script is ran from a bash script itself being piped from curl, when the prompt comes up, it is automatically "skipped" and everything ends like so:
$ curl -L http://install.example.com | bash
-=- Welcome ! -=-
We have detected you have python3 installed.
====> Confirm or enter installation directory [/srv/vhosts/project]: ====> Confirm installation [y/n]: Installation aborted.
As you can see, the script doesn't wait for user input, because of the pipe which ties the input to the curl output. Thus, we have the following problem:
curl [STDOUT]=>[STDIN] BASH (which executes python script)
= the [STDIN] of the python script is the [STDOUT] of curl (which contains at a EOF) !
How can I keep this very useful and short command line (curl -L http://install.example.com | bash) and still be able to prompt the user for input ? I should somehow detach the stdin of python from curl but I didn't find how to do it.
Thanks very much for your help !
Things I have also tried:
Starting the python script in a subshell: $(/usr/bin/env python3 deploy.py)
You can always redirect standard input from the controlling tty, assuming there is one:
/usr/bin/env python3 deploy_p3k.py < /dev/tty
or
/usr/bin/env python3 deploy_p3k.py <&1
I wrote a bash that has python command included in loop: (part of script)
#!/bin/bash
ARG=( $(echo "${#:3}"))
for (( i=1; i<=$#-3; i++ ))
do
python -c "print('<Command with variables>' * 1)"
done
When I run it, depends on number of my args for example I have this output:
nohup command-a &
nohup command-b &
nohup command-c &
How do I execute the output lines from bash immediately?
Can I tell python command to run them in each iteration? How?
Thank you
You can achieve that by executing the python code in a sub-shell and evaluating the content of that shell afterwards.
eval $(python -c ...)
$() returns a string you can evaluate with eval
You are asking two questions. I can only answer the first one:
If you want to run commands coming to the standard output, just redirect them to bash:
python -c 'print "ls"' | bash
I have some python-2.x scripts which I copy between different systems, Debian and Arch linux.
Debian install python as '/usr/bin/python' while Arch installs it as '/usr/bin/python2'.
A problem is that on Arch linux '/usr/bin/python' also exists which refers to python-3.x.
So every time I copy a file I have to correct the shebang line, which is a bit annoying.
On Arch I use
#!/usr/bin/env python2
While on debian I have
#!/usr/bin/env python
Since 'python2' does not exist on Debian, is there a way to pass a preferred application? Maybe with some shell expansion? I don't mind if it depends on '/bin/sh' existing for example.
The following would be nice but don't work.
#!/usr/bin/env python2 python
#!/usr/bin/env python{2,}
#!/bin/sh python{2,}
#!/bin/sh -c python{2,}
The frustrating thing is that 'sh -c python{2,}' works on the command line: i.e. it calls python2 where available and otherwise python.
I would prefer not to make a make a link 'python2->python' on Debian because then if I give the script to someone else it will not run. Neither would I like to make 'python' point to python2 on Arch, since it breaks with updates.
Is there a clean way to do this without writing a wrapper?
I realize similar question have been asked before, but I didn't see any answers meeting my boundary conditions :)
Conditional shebang line for different versions of Python
--- UPDATE
I hacked together an ugly shell solution, which does the job for now.
#!/bin/bash
pfound=false; v0=2; v1=6
for p in /{usr/,}bin/python*; do
v=($(python -V 2>&1 | cut -c 7- | sed 's/\./ /g'))
if [[ ${v[0]} -eq $v0 && ${v[1]} -eq $v1 ]]; then pfound=true; break; fi
done
if ! $pfound; then echo "no suitable python version (2.6.x) found."; exit 1; fi
$p - $* <<EOF
PYTHON SCRIPT GOES HERE
EOF
explanation:
get version number (v is a bash array) and check
v=($(python -V 2>&1 | cut -c 7- | sed 's/\./ /g'))
if [[ ${v[0]} -eq $v0 && ${v[1]} -eq $v1 ]]; then pfound=true; break; fi
launch found program $p with input from stdin (-) and pass arguments ($*)
$p - $* <<EOF
...
EOF
#!/bin/sh
''''which python2 >/dev/null 2>&1 && exec python2 "$0" "$#" # '''
''''which python >/dev/null 2>&1 && exec python "$0" "$#" # '''
''''exec echo "Error: I can't find python anywhere" # '''
import sys
print sys.argv
This is first run as a shell script. You can put almost any shell code in between '''' and # '''. Such code will be executed by the shell. Then, when python runs on the file, python will ignore the lines as they look like triple-quoted strings to python.
The shell script tests if the binary exists in the path with which python2 >/dev/null and then executes it if so (with all arguments in the right place). For more on this, see Why does this snippet with a shebang #!/bin/sh and exec python inside 4 single quotes work?
Note: The line starts with four ' and their must be no space between the fourth ' and the start of the shell command (which...)
Something like this:
#!/usr/bin/env python
import sys
import os
if sys.version_info >= (3, 0):
os.execvp("python2.7", ["python2.7", __file__])
os.execvp("python2.6", ["python2.6", __file__])
os.execvp("python2", ["python2", __file__])
print ("No sutable version of Python found")
exit(2)
Update Below is a more robust version of the same.
#!/bin/bash
ok=bad
for pyth in python python2.7 python2.6 python2; do
pypath=$(type -P $pyth)
if [[ -x $pypath ]] ; then
ok=$(
$pyth <<##
import sys
if sys.version_info < (3, 0):
print ("ok")
else:
print("bad")
##
)
if [[ $ok == ok ]] ; then
break
fi
fi
done
if [[ $ok != ok ]]; then
echo "Could not find suitable python version"
exit 2
fi
$pyth <<##
<<< your python script goes here >>>
##
I'll leave this here for future reference.
All of my own scripts are usually written for Python 3, so I'm using a modified version of Aaron McDaid's answer to check for Python 3 instead of 2:
#!/usr/bin/env sh
''''which python3 >/dev/null 2>&1 && exec python3 "$0" "$#" # '''
''''test $(python --version 2>&1 | cut -c 8) -eq 3 && exec python "$0" "$#" # '''
''''exec echo "Python 3 not found." # '''
import sys
print sys.argv
Here is a more concise way for the highest voted answer:
#!/bin/sh
''''exec $(which python3 || which python2 || echo python) $0 $# #'''
import sys
print(sys.argv)
print(sys.version_info)
You'll get this if none of them found:
'./test.py: 2: exec: python: not found'
Also, get rid of warnings from linter:
'module level import not at top of file - flake8(E402)'
In my case I don't need the python path or other information (like version).
I have an alias setup to point "python" to "python3". But, my scripts are not aware of the environment alias. Therefore I needed a solution for the scripts to determine the program name, programmatically.
If you have more than one version installed, the sort and tail are going to give you the latest version:
#!/bin/sh
command=$((which python3 || which python2 || which python) | sort | tail -n1 | awk -F "/" '{ print $NF }')
echo $command
Would give a result like: python3
This solution is original but similar to #Pamela