This recipe is a simple (very simple) batch process dispatcher. It allows you to utilize a simple file with a list of commands to run to make your command-line life a little easier. I use it for mencoder so I don't have to type/paste in a bunch of commands for backing up my DVDs. I can just write a batch file. And if there is an error somewhere along the way, or I need to stop for some reason, I can just start where I left off when I am ready.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | #!/bin/env python
# coding:utf-8
# vim:ts=4:enc=utf-8
"""
Simple batch file processing module
Format of batch file:
- A # is a comment delimiter, ignore everything on the line.
- Anything else is command to run - no line continuations!
You can basically just use a list of commands understood
by the shell.
Example batch file:
echo "Cmd 1" # this is a comment
df -h
# this is a comment too
ls -lh
echo "Cmd 4"
Simple, eh?
ver 1.1 - slight cleanup
ver 1.2 - remove interactive jobs (causing some bugs),
rename variables to be more intuitive, add comments
If you really want to have interactive jobs, you could use
something like this bit of ugliness (but all on one line!):
read -t 30 -p "Run command? [Y]"; if [ -z ${REPLY} ] ||
[ ${REPLY} != "N" ]; then echo "I like cheese"; fi
ver 1.3 - minor cleanup
"""
import os
import sys
class SimpleBatchFile:
"""
This is the main class
Instantiate it then call run()
"""
def _exec(self, command, jobnum, linenum, batchfile):
"""Execute a command in a subshell, die on errors"""
print(' Starting job: %s\nExecuting command: %s\n' \
% (jobnum, command))
rv = os.system(command)
if (rv):
print('Problem running job...\n'
'File: %s\n'
' Job: %d\n'
'Line: %d\n'
'...dieing!' % (batchfile, jobnum, linenum))
sys.exit(rv)
def __init__(self, batchfile):
"""Start the fun"""
if not (os.path.isfile(batchfile)):
print('Cannot find batch file: %s' % batchfile)
sys.exit(1)
self.file = batchfile
fd = open(batchfile, 'rb')
data = fd.readlines()
fd.close()
self.jobs = []
for i in range(0, len(data)):
item = data[i].strip()
## skip comments and empty lines
if (item.startswith('#', 0, 1) or item == ''):
continue
## format for jobs is (command, jobnum, linenum)
self.jobs.append((item, len(self.jobs) + 1, i + 1))
self.jobcount = len(self.jobs)
def list(self):
"""List jobs and so forth"""
for i in range(0, self.jobcount):
if (i == 0):
pad = ''
else:
pad = '========\n'
print('%s Job: %s\n Line: %s\nCommand: %s' \
% (pad,
self.jobs[i][1],
self.jobs[i][2],
self.jobs[i][0]))
def run(self, start=1, end=None):
"""
to start from a given job use n, e.g., 3
or to run a range of jobs use n:n, e.g., 3:5
or for a single job only use n:n, e.g., 2:2
no args means all jobs in batch are run
"""
## some bounds checking and logics
if not (end):
end = self.jobcount
if (start < 1):
start = 1
if (start > self.jobcount):
start = self.jobcount
if (end > self.jobcount):
end = self.jobcount
if (end <= start):
end = start
for i in range(start - 1, end):
self._exec(self.jobs[i][0],
self.jobs[i][1],
self.jobs[i][2],
self.file)
## end class SimpleBatchFile
if (__name__ == '__main__'):
## standalone script
def get(lst, idx):
"""Return the value of lst[idx] or None"""
if (len(lst) > idx):
return lst[idx]
else:
return None
## get batch file name from argv
bfnm = get(sys.argv, 1)
## no file given, print usage and exit
if not (bfnm):
me = os.path.basename(sys.argv[0])
print('Usage: %s batch_file [start_job_number]' % me)
sys.exit(1)
## instantiate class
sbf = SimpleBatchFile(bfnm)
## get range from argv
scmd = get(sys.argv, 2)
if not (scmd):
## run all jobs
sbf.run()
elif ('-l' in scmd):
## show job list
sbf.list()
elif (':' in scmd):
## run range of jobs
scmd, ecmd = scmd.split(':')
if not (ecmd):
ecmd = 0
sbf.run(int(scmd), int(ecmd))
else:
## run all jobs including and after
sbf.run(int(scmd))
|
This lets you run a single job (e.g., "1:1"), a range of jobs (e.g., "1:3"), start with one job and then do all jobs after that one (e.g., "7"), or run all jobs from start to finish (no input). Also, passing -l will call the list method and show you each job, the command it runs and what line it appears on in the batch file.
Nota bene: Jobs are indexed from 1, not 0. So if you want to run the second job use "2:2" rather than "1:1". Use -l to see all jobs and ther slots.