Answer
Let's break it down into pieces. Especially the pieces you got wrong. :)
Assignment
outfile=ReadsAgain.txt
It should come to little surprise that you need to put quotes around strings. On the other hand, you have the luxury of putting spaces around the = for readability.
outfilename = "ReadsAgain.txt"
Variable expansion → str.format (or, the % operation)
python reads.py <snip/> -q$queries <snip/>
So you know how to do the redirection already, but how do you do the variable expansion? You can use the format method (v2.6+):
command = "python reads.py -r1 -pquery1.sql -q{0} -shotelspec -k6 -a5".format(queries)
You can alternatively use the % operator:
#since queries is a number, use %d as a placeholder
command = "python reads.py -r1 -pquery1.sql -q%d -shotelspec -k6 -a5" % queries
C-style loop → Object-oriented-style loop
for ((r = 1; r < ($runs + 1); r++)) do done
Looping in Python is different from C-style iteration. What happens in Python is you iterate over an iterable object, like for example a list. Here, you are trying to do something runs times, so you would do this:
for r in range(runs):
#loop body here
range(runs) is equivalent to [0,1,...,runs-1], a list of runs = 5 integer elements. So you'll be repeating the body runs times. At every cicle, r is assigned the next item of the list. This is thus completely equivalent to what you are doing in Bash.
If you're feeling daring, use xrange instead. It's completely equivalent but uses more advanced language features (so it is harder to explain in layman's terms) but consumes less resources.
Output redirection → the subprocess module
The "tougher" part, if you will: executing a program and getting its output. Google to the rescue! Obviously, the top hit is a stackoverflow question: this one. You can hide all the complexity behind it with a simple function:
import subprocess, shlex
def get_output_of(command):
args = shlex.split(command)
return subprocess.Popen(args,
stdout=subprocess.PIPE).communicate()[0]
# this only returns stdout
So:
python reads.py -r1 -pquery1.sql -q$queries -shotelspec -k6 -a5 >> $outfile
becomes:
command = "python reads.py -r1 -pquery1.sql -q%s -shotelspec -k6 -a5" % queries
read_result = get_output_of(command)
Don't over-subprocess, batteries are included
Optionally, consider that you can get pretty much the same output of date with the following:
import time
time_now = time.strftime("%c", time.localtime()) # Sat May 15 15:42:47 2010
(Note the absence of the time zone information. This should be the subject of another question, if it is important to you.)
How your program should look like
The final result should then look like this:
import subprocess, shlex, time
def get_output_of(command):
#... body of get_output_of
#... more functions ...
if __name__ = "__main__":
#only execute the following if you are calling this .py file directly,
#and not, say, importing it
#... initialization ...
with file("outputfile.txt", "a") as output_file: #alternative way to open files, v2.5+
#... write date and other stuff ...
for r in range(runs):
#... loop body here ...
Post scriptum
That must look pretty horrible when compared to the relatively simple and short Bash script, right? Python is not a specialized language: it aims to do everything reasonably well, but isn't built directly for running programs and getting the output of those.
Still, you wouldn't normally write a database engine in Bash, right? It's different tools for different jobs. Here, unless you're planning to make some changes that would be non-trivial to write with that language, [Ba]sh was definitely the right choice.
Answer from badp on Stack OverflowAutomatically converting (most of?) n bash scripts to Python?
Converting a bash script to python (small script) - Stack Overflow
Converting the complete environment from Bash to Python because of portability
From Bash to Python
Videos
Answer
Let's break it down into pieces. Especially the pieces you got wrong. :)
Assignment
outfile=ReadsAgain.txt
It should come to little surprise that you need to put quotes around strings. On the other hand, you have the luxury of putting spaces around the = for readability.
outfilename = "ReadsAgain.txt"
Variable expansion → str.format (or, the % operation)
python reads.py <snip/> -q$queries <snip/>
So you know how to do the redirection already, but how do you do the variable expansion? You can use the format method (v2.6+):
command = "python reads.py -r1 -pquery1.sql -q{0} -shotelspec -k6 -a5".format(queries)
You can alternatively use the % operator:
#since queries is a number, use %d as a placeholder
command = "python reads.py -r1 -pquery1.sql -q%d -shotelspec -k6 -a5" % queries
C-style loop → Object-oriented-style loop
for ((r = 1; r < ($runs + 1); r++)) do done
Looping in Python is different from C-style iteration. What happens in Python is you iterate over an iterable object, like for example a list. Here, you are trying to do something runs times, so you would do this:
for r in range(runs):
#loop body here
range(runs) is equivalent to [0,1,...,runs-1], a list of runs = 5 integer elements. So you'll be repeating the body runs times. At every cicle, r is assigned the next item of the list. This is thus completely equivalent to what you are doing in Bash.
If you're feeling daring, use xrange instead. It's completely equivalent but uses more advanced language features (so it is harder to explain in layman's terms) but consumes less resources.
Output redirection → the subprocess module
The "tougher" part, if you will: executing a program and getting its output. Google to the rescue! Obviously, the top hit is a stackoverflow question: this one. You can hide all the complexity behind it with a simple function:
import subprocess, shlex
def get_output_of(command):
args = shlex.split(command)
return subprocess.Popen(args,
stdout=subprocess.PIPE).communicate()[0]
# this only returns stdout
So:
python reads.py -r1 -pquery1.sql -q$queries -shotelspec -k6 -a5 >> $outfile
becomes:
command = "python reads.py -r1 -pquery1.sql -q%s -shotelspec -k6 -a5" % queries
read_result = get_output_of(command)
Don't over-subprocess, batteries are included
Optionally, consider that you can get pretty much the same output of date with the following:
import time
time_now = time.strftime("%c", time.localtime()) # Sat May 15 15:42:47 2010
(Note the absence of the time zone information. This should be the subject of another question, if it is important to you.)
How your program should look like
The final result should then look like this:
import subprocess, shlex, time
def get_output_of(command):
#... body of get_output_of
#... more functions ...
if __name__ = "__main__":
#only execute the following if you are calling this .py file directly,
#and not, say, importing it
#... initialization ...
with file("outputfile.txt", "a") as output_file: #alternative way to open files, v2.5+
#... write date and other stuff ...
for r in range(runs):
#... loop body here ...
Post scriptum
That must look pretty horrible when compared to the relatively simple and short Bash script, right? Python is not a specialized language: it aims to do everything reasonably well, but isn't built directly for running programs and getting the output of those.
Still, you wouldn't normally write a database engine in Bash, right? It's different tools for different jobs. Here, unless you're planning to make some changes that would be non-trivial to write with that language, [Ba]sh was definitely the right choice.
It should be fairly simple to port your program. The only tricky part will be running the db2 command and (maybe) refactoring reads.py so that it can be called as a library function.
The basic idea is the same:
- Setting local variables is the same.
- Replace
echowithprint. - Replace your loop with
for r in range(runs):. - Get the
datewith the datetime module. - Replace write to file with the file objects module.
- Replace the call to
db2with the subprocess module. - You'll need to
import reads.pyto use as a library (or you can use subprocess).
But, as Marcelo says, if you want more help- you're best off putting in some effort of your own to ask direct questions.
Just wondering If it's possible
Just wondering I have a simple bash script that I would lie to convert
import os
for i in range(1, 50):
env_var = os.environ[f'{i}nl']
os.system(f"ssh BF-c{env_var} 'hostname; free -h; uname -a;'")
Python3.6 >
env_var = os.environ[str(i) + 'nl']
os.system("ssh BF-c{} 'hostname; free -h; uname -a;'".format(env_var))
You can do it using Bash2Py.
Also, you can try using the docker image
Courtesy: Bash to Python
So the below code is actually the complete script for conversion.
#! /usr/bin/env python
from __future__ import print_function
import sys,os
class Bash2Py(object):
__slots__ = ["val"]
def __init__(self, value=''):
self.val = value
def setValue(self, value=None):
self.val = value
return value
def GetVariable(name, local=locals()):
if name in local:
return local[name]
if name in globals():
return globals()[name]
return None
def Make(name, local=locals()):
ret = GetVariable(name, local)
if ret is None:
ret = Bash2Py(0)
globals()[name] = ret
return ret
def Array(value):
if isinstance(value, list):
return value
if isinstance(value, basestring):
return value.strip().split(' ')
return [ value ]
class Expand(object):
@staticmethod
def at():
if (len(sys.argv) < 2):
return []
return sys.argv[1:]
@staticmethod
def star(in_quotes):
if (in_quotes):
if (len(sys.argv) < 2):
return ""
return " ".join(sys.argv[1:])
return Expand.at()
@staticmethod
def hash():
return len(sys.argv)-1
if (Expand.hash() < 1 ):
print("Usage: "+__file__+" file ...")
exit(1)
print(__file__+" counts the lines of code")
l=Bash2Py(0)
for Make("f").val in Expand.star(0):
Make("l").setValue(os.popen("wc -l "+str(f.val)+" | sed \"s/^\\([0-9]*\\).*$/\\1/\"").read().rstrip("\n"))
print(str(f.val)+": "+str(l.val))
The guts of the code is in the for loop at the bottom.
bash2py does some safe conversion and wrapping of the bash script into some methods such as ‘Make’, ‘Array’ et al that we can get rid of with a little work.
By replacing:
Bash2Py(0)with0Make(“f”).valwithfandMake(“l”)withletcf.valwithfandl.valwithletc