Jump to content

[Python] Shell Multi Runner (cli) [cmiN]

Recommended Posts

Acest script cross-platform permite rularea unei comenzi batch/bash cu parametri variabili preluati din fisiere text, linie cu linie. Am simtit nevoia sa fac ceva mai general tocmai din cauza multor subiecte si cereri pe tema asta.

Indiferent cate comenzi veti executa tot outputul e afisat in timp real in aceeasi consola (sau si intr-un fisier) fara sa se amestece (se presupune a folosi comenzi de aceeasi speta ce genereaza un output calitativ nu cantitativ), iar preluarea comenzilor este foarte stabila, respecta cu strictete numarul threadurilor alocate si ordinea in functie de timpi.

Codul este pur Python, pana si executarea comenzilor se face in procese separate independente de terminal, ceea ce previne shell injection si alte neplaceri cu restrictia unor "smenuri" tipice bash, dar acest comportament poate fi schimbat prin modificarea si adaugarea unui argument din clasa Popen, oricum nu intru in amanunte, fiindca e in afara scopului si nici nu cred ca va veti lovi de problema asta.

Foloseste Python 2.x, testat pe windows 7 si backtrack cu un script simplu ca:

#include <stdio.h>
#include <time.h>
#include <windows.h>
#define N 10 /* N phases */

int main(int argc, char* argv[])
int i;
for (i = 0; i < N; ++i) {
printf("Process %s with %s at phase %d.\n", argv[1], argv[2], i);
Sleep(1000); /* replace with sleep(1) on posix */
return 0;

Parametrii de test luati din 2 fisiere prin linia: run.py -t 2 -d 0.5 scan.exe @a.txt @b.txt

P.S.: Atentie la output, imaginati-va putin cam cum va arata ceea ce urmeaza sa faceti ca sa nu aveti surprize.

cd in folderul cu scriptul

chmod +x ./run.py

./run.py -> vezi usage


#! /usr/bin/env python
# Shell Multi Runner
# 12.02.2012 cmiN
# Execute commands in terminal asynchronously using subprocess
# and show all output merged into one console.
# Contact: cmin764@yahoo/gmail.com

import subprocess # better than popen/system/respawn
from sys import argv, stdout
from time import sleep
from threading import active_count, Thread # parallelism

# some settings
FILE = None # output to file too
THRD = 10 # threads
DLAY = 1 # delay
CHAR = '@' # wildcard

# instantiated in only one object
class Show(file):
Thread safe printing class.
Uses primitive locks.

def __init__(self, fname=None):
""" If `fname` isn't `None` write output to file too. """
self.locked = False # unlocked

def __del__(self):
""" Destructor. Close an opened file. """
if self.fname:

def open_file(self, fname):
""" Open file for writing. """
self.fname = fname
if fname: # init file
super(Show, self).__init__(fname, 'w')

def write(self, data):
""" Safe write. """
while self.locked: # if writing in progress
pass # wait
# lock
self.locked = True
# write data
if self.fname:
super(Show, self).write(data)
# flush data
if self.fname:
# release
self.locked = False

def fileno(self):
""" Experimental. Used as file descriptor replacing pipes. """
if self.fname:
return super(Show, self).fileno()
return stdout.fileno()

class Engine(Thread):
Execute each command in a separate thread
and listen for it's output.

def __init__(self, command):
super(Engine, self).__init__() # superclass constructor
self.command = command

def run(self):
""" Function called from outside by `start` method. """
# fork the fucking process
pobj = subprocess.Popen(self.command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# listen for new input
while True:
line = pobj.stdout.readline()
if line == "": # more output it's about to come
if pobj.poll() != None: # nope
break # so exit
continue # try again

# globals
usage = """
Usage: {0} [options] command

-t, --threads <int> how many asynchronous threads to run
-d, --delay <float> time in seconds to wait between each run
-f, --file <str> write output to file too

<any valid command> ex: wget {1}links.txt

If you preceed a parameter with {1} it becomes a list with parameters
taken from a file called like itself.
Old: ./scan -h -u usr.txt -p pwd.txt
New: {0} ./scan -h {1}hosts.txt -u usr.txt -p pwd.txt
""".format(argv[0], CHAR)
report = Show() # make verbose object

def generate(command, expand, pos):
""" Format command recursively. """
if pos == len(expand):
# now command string is complete
sleep(DLAY) # delay
while active_count() > THRD:
pass # wait if number of threads is exceeded
report.write("[+] Start: %s\n" % command)
expand[pos].seek(0) # rewind
for line in expand[pos]:
generate(command.replace("{%d}" % pos, line.strip()), expand, pos + 1)

def main():
# check
if len(argv) == 1 or argv[1] in ('-h', "--help"):
print usage
return # insuficient parameters
# parse
report.write("[+] Parsing...\n")
argv.pop(0) # remove script name
command = ""
ind = 0 # index
expand = [] # list with special parameters
while ind < len(argv):
if argv[ind] in ('-t', "--threads"):
ind += 1
THRD = int(argv[ind])
elif argv[ind] in ('-d', "--delay"):
ind += 1
DLAY = float(argv[ind])
elif argv[ind] in ('-f', "--file"):
ind += 1
FILE = argv[ind]
elif argv[ind][0] == CHAR:
# reserve variable parameter for special ones
command += ' ' + "{%d}" % (len(expand))
# add to list special parameters (`CHAR`<smth>)
expand.append(open(argv[ind][1:], 'r')) # file objects
command += ' ' + argv[ind]
ind += 1
# process
report.write("[+] Processing...\n")
generate(command.strip(), expand, 0)
while active_count() > 1:
pass # wait for running threads
report.write("[+] Done.\n")

if __name__ == "__main__":

Updated: 14.02.2012

Edited by cmiN
  • Upvote 1

Share this post

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...