Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何从一个子进程同步运行多个命令.Popen命令?_Python_Subprocess_Python 3.5_Python 2.6 - Fatal编程技术网

Python 如何从一个子进程同步运行多个命令.Popen命令?

Python 如何从一个子进程同步运行多个命令.Popen命令?,python,subprocess,python-3.5,python-2.6,Python,Subprocess,Python 3.5,Python 2.6,是否可以使用相同的子流程命令按顺序执行任意数量的命令 我需要每个命令在执行之前等待前一个命令完成,并且我需要它们都在同一个会话/shell中执行。在Python2.6和Python3.5中,我也需要它。我还需要subprocess命令在Linux、Windows和macOS中工作(这就是为什么我在这里仅使用echo命令作为示例) 请参见下面的非工作代码: import sys import subprocess cmds = ['echo start', 'echo mid', 'echo e

是否可以使用相同的子流程命令按顺序执行任意数量的命令

我需要每个命令在执行之前等待前一个命令完成,并且我需要它们都在同一个会话/shell中执行。在Python2.6和Python3.5中,我也需要它。我还需要subprocess命令在Linux、Windows和macOS中工作(这就是为什么我在这里仅使用
echo
命令作为示例)

请参见下面的非工作代码:

import sys
import subprocess

cmds = ['echo start', 'echo mid', 'echo end']

p = subprocess.Popen(cmd=tuple([item for item in cmds]),
                     stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

for line in iter(p.stdout.readline, b''):
    sys.stdout.flush()
    print(">>> " + line.rstrip())

如果这是不可能的,我应该采取哪种方法才能在同一个会话/shell中以同步顺序执行命令?

一种可能的解决方案看起来像是在同一个shell中运行:

subprocess.Popen('echo start;echo mid;echo end', shell=True)
注意-如果以字符串形式传递命令,则shell必须为True 注意-这仅适用于linux,您可能需要在windows上找到类似的方法

希望这会有帮助

从python文档-

在shell=True的Unix上,shell默认为/bin/sh 字符串,该字符串指定要通过shell执行的命令。 这意味着字符串的格式必须与实际格式完全相同 在shell提示下键入时


这个可以在Python2.7中使用,也可以在windows中使用。python>3可能需要一些小的改进

生成的输出是(使用date和sleep很容易看出命令是在行中执行的):

如您所见,这些命令是按行执行的

    import sys
    import subprocess
    import shlex

    cmds = ['date', 'sleep 2', 'date']

    cmds = [shlex.split(x) for x in cmds]

    outputs =[]
    for cmd in cmds:
        outputs.append(subprocess.Popen(cmd,
                                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate())


    for line in outputs:
        print ">>>" + line[0].strip()
这是我通过@Marichyasana-answer获得的结果: 问:这个不适合你的需要吗!;)

这里是我使用的一个函数(以及运行它的main)。我想说你可以用它来解决你的问题。它是灵活的

# processJobsInAList.py
# 2016-09-27   7:00:00 AM   Central Daylight Time 

import win32process, win32event

def CreateMyProcess2(cmd):
    ''' create process width no window that runs a command with arguments
    and returns the process handle'''
    si   = win32process.STARTUPINFO()
    info = win32process.CreateProcess(
        None,      # AppName
        cmd,       # Command line
        None,      # Process Security
        None,      # Thread Security
        0,         # inherit Handles?
        win32process.NORMAL_PRIORITY_CLASS,
        None,      # New environment
        None,      # Current directory
        si)        # startup info
    # info is tuple (hProcess, hThread, processId, threadId)
    return info[0]

if __name__ == '__main__' :
    ''' create/run a process for each list element in "cmds"
    output may be out of order because processes run concurrently '''

    cmds=["echo my","echo heart","echo belongs","echo to","echo daddy"]
    handles    = []
    for i in range(len(cmds)):
        cmd    = 'cmd /c ' + cmds[i]
        handle = CreateMyProcess2(cmd)
        handles.append(handle)

    rc = win32event.WaitForMultipleObjects( handles, 1, -1)  # 1 wait for all, -1 wait infinite
    print 'return code ',rc
输出:
心脏
我的
属于

爸爸
返回代码0

更新:如果要运行相同的进程,它将为您序列化内容:
1) 删除行:句柄。追加(句柄)
2) 用变量“handle”代替“WaitFor”行上的列表“handles”

3) 将WaitForSingleObject替换为WaitForMultipleObjects

如果要在同一会话/shell中依次执行多个命令,则必须启动一个shell并向其提供所有命令,一次一个,后跟新行,最后关闭管道。如果某些命令不是真正的进程,而是可能改变shell环境的shell命令,那么这是有意义的

在Windows下使用Python 2.7的示例:

encoding = 'latin1'
p = subprocess.Popen('cmd.exe', stdin=subprocess.PIPE,
             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for cmd in cmds:
    p.stdin.write(cmd + "\n")
p.stdin.close()
print p.stdout.read()
要在Linux下运行此代码,您必须将
cmd.exe
替换为
/bin/bash
,并可能将编码更改为utf8

对于Python3,您必须对命令进行编码,可能还要对其输出进行解码,并在打印时使用括号


注意:这只适用于很少的输出。如果在关闭stdin管道之前有足够的输出填充管道缓冲区,则此代码将死锁。更可靠的方法是使用第二个线程读取命令的输出,以避免该问题。

这与Serge Ballesta发布的答案类似,但不完全相同。将his用于异步执行,您不关心结果。使用mine进行同步处理和结果收集。和他的答案一样,我在这里展示的是Windows解决方案——在Linux中运行bash进程,而不是在Windows中运行cmd

from subprocess import Popen, PIPE
process = Popen( "cmd.exe", shell=False, universal_newlines=True,
                  stdin=PIPE, stdout=PIPE, stderr=PIPE )                             
out, err = process.communicate( commands ) 
用法详细信息:此处传递给
进程的
命令
参数。communicate
方法是一个以新行分隔的字符串。例如,如果您只是将批处理文件内容读入字符串,则可以这样运行,因为它已经有了换行符

重要:字符串必须以换行符
“\n”
结尾。否则,最终命令将无法执行。就像您在命令提示符中键入它,但最后没有点击
enter
。但是,您将在返回的标准输出的末尾看到一个神秘的
More?
行。(如果遇到这种情况,这就是原因)

process.communicate
根据定义同步运行,并返回stdout和stderr消息(如果将它们定向到Popen构造函数中的
subprocess.PIPE

以这种方式创建
cmd.exe
进程并向其传递字符串时,结果将与打开命令提示窗口并在中输入命令完全相同。我的意思是说,这完全是字面意思。如果您对此进行测试,您将看到返回的stdout包含您的命令。(与执行批处理文件一样,是否包含
@echo off
并不重要)

对于那些关心“干净”标准测试结果的人的提示:

  • @echo off
    不会禁止您的命令出现在此返回的字符串中,但它确实会删除额外的换行符。(universal_newlines=True表示另一组)

  • 在命令中添加
    @
    符号前缀,使它们仍能执行。在“正常”批处理过程中,这是逐行“隐藏”命令的方式。在这种情况下,它是一个安全的、简单的标记,通过它可以找到要删除的标准行。(如果有人这么想的话)

  • cmd.exe“标题”将出现在输出中(表示Windows等的版本)。由于您可能希望在启动命令集时使用
    @echo off
    ,以删除多余的换行符,因此这也是找到标题行停止位置和命令/结果开始位置的好方法

最后,为了解决“大”输出填充管道并给您带来问题的担忧——首先,我认为您需要返回大量数据才能成为问题——这比大多数人在使用案例中遇到的要多。第二,如果它真的是c
encoding = 'latin1'
p = subprocess.Popen('cmd.exe', stdin=subprocess.PIPE,
             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for cmd in cmds:
    p.stdin.write(cmd + "\n")
p.stdin.close()
print p.stdout.read()
from subprocess import Popen, PIPE
process = Popen( "cmd.exe", shell=False, universal_newlines=True,
                  stdin=PIPE, stdout=PIPE, stderr=PIPE )                             
out, err = process.communicate( commands ) 
import glob
import os
import pathlib
import traceback
import logging
from datetime import datetime
from subprocess import Popen, PIPE, STDOUT
from threading import Thread, Event
from typing import Union
from enum import Enum

LOG_FOLDER = "{0}\{1}".format(pathlib.Path().absolute(), "log")
GDB_FOLDER = "C:\\MinGW\\bin"
GDB_EXE = "gdb.exe"
GDB_PY_EXE = "gdb-python27.exe"
CMD_EXE = "C:\\Windows\\system32\\cmd.exe"
CALC_EXE = "C:\\Windows\\system32\\win32calc.exe"


class LOG_TYPE(Enum):
    Info = 0,
    Warning = 1,
    Error = 2,
    Critical = 3


class STD_LOG(object):
    def __init__(self, name: str, log_enabled: bool = True, print_enabled: bool = True,
                 detailed_log: bool = False) -> None:
        self.log_enabled = log_enabled
        self.print_enabled = print_enabled
        self.filename = "{0}\{1}{2}.log".format(LOG_FOLDER, name,
                                                datetime.now().strftime("-%d_%m_%Y"))  # "-%d_%m_%Y-%H_%M_%S"
        self.logger = logging.getLogger('CLI_LOGGER')
        self.logger.setLevel(logging.DEBUG)
        handler = logging.FileHandler(filename=self.filename, mode="a", encoding="utf-8")
        formatter = logging.Formatter('%(message)s')
        if detailed_log:
            formatter = logging.Formatter('%(asctime)s \t %(name)s \t %(levelname)s: \n%(message)s \n')
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def output(self, data: str, logtype: LOG_TYPE = LOG_TYPE.Info):
        if not data:
            return None
        if self.print_enabled:
            print(data)
        if self.log_enabled:
            if logtype == LOG_TYPE.Info:
                self.logger.info(msg=data)
            elif logtype == LOG_TYPE.Warning:
                self.logger.warning(msg=data)
            elif logtype == LOG_TYPE.Error:
                self.logger.error(msg=data)
            else:
                self.logger.critical(msg=data)

        # FOR STACKOVERFLOW Example
        # if api_call activated => run api call from a server
        # if output == "desired result":
        #   Do something
        # Etc.

    def input(self, data: str):
        pass
        # FOR STACKOVERFLOW
        # Perhaps a separate log file?
        # Or redirect to output, etc.
        # self.output(data=data)


# inspiration for killable thread -> https://stackoverflow.com/a/49877671
class CLI_THREAD(Thread):
    def __init__(self, source: str, logger: STD_LOG, sleep_interval: float = 0.25) -> None:
        super().__init__()
        self._close = Event()
        self._interval = sleep_interval
        self.base = Popen(source, stdin=PIPE, stdout=PIPE, stderr=STDOUT, text=True, shell=True, close_fds=True)
        self.logger = logger
        self.logger.output(data="CLI STARTED [Process ID: {0}]".format(self.pid()))

    def cli_alive(self) -> Union[bool, int]:
        if self.base.poll() is None:
            return True
        else:
            return False

    def pid(self) -> int:
        return self.base.pid

    def run(self) -> None:
        while True:
            try:
                if not self.cli_alive():
                    break

                if self.base.stdout.readable():
                    output = self.base.stdout.readline().strip()
                    self.logger.output(data=output)

                closing = self._close.wait(self._interval)
                if closing and self.base.stdout.closed:
                    break
            except Exception as ex:
                ex_msg = ''.join(traceback.format_exception(None, ex, ex.__traceback__))
                self.logger.output(data=ex_msg, logtype=LOG_TYPE.Error)

        self.logger.output(data="End of CLI Thread")

    def close(self) -> None:
        self._close.set()

    def terminate(self) -> None:
        if self.cli_alive():
            self.base.terminate()
            self.logger.output(data="Terminate function activated", logtype=LOG_TYPE.Warning)


class CLI(object):
    def __init__(self, name: str, source: str, close_arg: str = None, echo: bool = True) -> None:
        self.logger = STD_LOG(name)
        self._cli_thread = CLI_THREAD(source=source, logger=self.logger)
        self._close_arg = close_arg
        self._cli_thread.start()  # start listening to console
        if not echo:
            self.execute("@echo off")

    def close(self):
        if self._close_arg:
            self.execute(command=self._close_arg)
        self._cli_thread.close()

    def cleanup(self):
        self._cli_thread.base.terminate()
        del self._cli_thread

    def __exit__(self, exc_type, exc_value, traceback):
        self.cleanup()

    def execute(self, command: str):
        if self._cli_thread.is_alive():
            self._cli_thread.base.stdin.write(command + "\n")
            self._cli_thread.base.stdin.flush()
        else:
            self.logger.output(data="Sending command to CLOSED THREAD", logtype=LOG_TYPE.Error)


def empty_log():
    files = glob.glob("{0}/*".format(LOG_FOLDER))
    for f in files:
        os.remove(f)


def main():
    cli = CLI(name="cli_1", source=CMD_EXE, close_arg="exit")
    try:
        cli.execute(command="cd {0}".format(GDB_FOLDER))
        cli.execute(command=GDB_EXE)
        cli.execute(command="file C:/Windows/system32/win32calc.exe")
        cli.execute(command="quit")
        cli.close()
    except Exception as e:
        msg = ''.join(traceback.format_exception(None, e, e.__traceback__))
        cli.logger.output(data=msg, logtype=LOG_TYPE.Critical)
        cli.cleanup()


if __name__ == '__main__':
    empty_log()
    main()