Нужен лучший способ выполнения команд консоли из python и регистрации результатов

У меня есть скрипт python, который должен выполнять несколько утилит командной строки. Вывод stdout иногда используется для дальнейшей обработки. Во всех случаях я хочу регистрировать результаты и возбуждать исключение, если обнаружена ошибка. Для достижения этой цели я использую следующую функцию:

def execute(cmd, logsink): logsink.log("executing: %s\n" % cmd) popen_obj = subprocess.Popen(\ cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = popen_obj.communicate() returncode = popen_obj.returncode if (returncode <> 0): logsink.log(" RETURN CODE: %s\n" % str(returncode)) if (len(stdout.strip()) > 0): logsink.log(" STDOUT:\n%s\n" % stdout) if (len(stderr.strip()) > 0): logsink.log(" STDERR:\n%s\n" % stderr) if (returncode <> 0): raise Exception, "execute failed with error output:\n%s" % stderr return stdout 

«logsink» может быть любым объектом python с методом журнала. Обычно я использую это для пересылки данных ведения журнала в конкретный файл или эха на консоль, или и того, и другого, или что-то еще …

Это работает довольно хорошо, за исключением трех проблем, когда мне нужен более мелкозернистый контроль, чем метод communication () обеспечивает:

  1. Вывод stdout и stderr может чередоваться на консоли, но вышеуказанная функция записывает их отдельно. Это может усложнить интерпретацию журнала. Как записывать строки stdout и stderr с чередованием в том же порядке, в каком они были выведены?
  2. Вышеуказанная функция будет только регистрировать выход команды после завершения команды. Это усложняет диагностику проблем, когда команды застревают в бесконечном цикле или занимают очень много времени по какой-то другой причине. Как получить журнал в режиме реального времени, пока команда все еще выполняется?
  3. Если журналы велики, может быть сложно интерпретировать, какая команда сгенерирована. Есть ли способ префикс каждой строки чем-то (например, первое слово строки cmd, за которым следует :).

Вы можете перенаправить файл, если хотите получить результат в файле для последующей оценки.

Вы уже определяете stdout / stderr процессов, которые вы выполняете с помощью методов stdout = / stderr =.

В вашем примере кода вы просто перенаправляете на скрипты current out / err assigments.

 subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

sys.stdout и sys.stderr – это только файлы-подобные объекты. Как упоминается документация документации по sys.stdout , «Любой объект допустим, если он имеет метод write (), который принимает строковый аргумент».

 f = open('cmd_fileoutput.txt', 'w') subprocess.Popen(cmd, shell=True, stdout=f, stderr=f) 

Таким образом, вам нужно только дать ему класс с методом записи, чтобы перенаправить вывод.

Если вы хотите, чтобы и вывод консоли, и выход файла, могли создавать класс для управления выходом.

Общее перенаправление:

 # Redirecting stdout and stderr to a file f = open('log.txt', 'w') sys.stdout = f sys.stderr = f 

Создание класса перенаправления:

 # redirecting to both class OutputManager: def __init__(self, filename, console): self.f = open(filename, 'w') self.con = console def write(self, data): self.con.write(data) self.f.write(data) new_stdout = OutputManager("log.txt", sys.stdout) 

Перемежение зависит от буферизации, поэтому вы можете или не можете получить ожидаемый результат. (Возможно, вы можете отключить или уменьшить используемую буферизацию, но я не помню, как на данный момент)

Вы можете посмотреть pexpect ( http://www.noah.org/wiki/Pexpect )

Он решает 1) и 2) из ​​коробки, префикс вывода может быть немного сложнее.

Еще один вариант:

 def run_test(test_cmd): with tempfile.TemporaryFile() as cmd_out: proc = subprocess.Popen(test_cmd, stdout=cmd_out, stderr=cmd_out) proc.wait() cmd_out.seek(0) output = "".join(cmd_out.readlines()) return (proc.returncode, output) 

Это будет чередовать stdout и stderr по желанию, в реальном файле, который вам удобно открыть.

Это отнюдь не полный или исчерпывающий ответ, но, возможно, вам следует изучить модуль Fabric.

http://docs.fabfile.org/0.9.1/

Делает параллельное выполнение команд оболочки и обработку ошибок довольно легко.