【问题标题】:Making python wait until subprocess.call has finished its command让 python 等到 subprocess.call 完成它的命令
【发布时间】:2020-02-14 10:00:33
【问题描述】:

所以这个问题是从this 开始的(阅读 cmets 以及那是我采取的路径)。我只希望第一个调用 robocopy 在继续执行其余代码之前完成执行。因为我希望第二个 robocopy 跳过所有文件,因为它们已经被复制了。然而,正在发生的事情是脚本的其余部分将运行(即启动第二个 robocopy),而第一个 robocopy 正在复制文件。下面是代码:

call(["start", "cmd", "/K", "RoboCopy.exe", f"{self.srcEntry.get()}", f"{self.dstEntry.get()}", "*.*", "/E", "/Z", "/MT:8"], stdout=PIPE, shell=True) 
temp2 = Popen(["RoboCopy.exe", f"{self.srcEntry.get()}", f"{self.dstEntry.get()}", "*.*", "/E", "/Z"], stdout=PIPE, stdin=PIPE, shell=True)

编辑 1:

复制大文件时问题很明显。我正在考虑添加一个睡眠功能,该功能取决于要复制的文件的总大小。但是,这没有考虑上传/下载速度,因为文件将通过网络传输。

【问题讨论】:

  • 你可以用subprocess.run代替Popen。 Link
  • 尝试实现 subprocess.run 但是当我运行 RoboCopy 时,我在 tkinter C:\Users\mnazir\Downloads\R-test 中将以下内容作为源路径并将其生成为源终端路径 C:\Users\mnazir\Desktop\Shortcuts 64bit\UsersmnazirDownloadsR-test\
  • 想出了我在运行命令时遇到的问题,但它仍然不能解决我的问题。一旦开始复制最后一个文件,第二个 Robocopy 似乎就开始了。如果我在 Robocopy 中使用 MT 选项,那么它几乎会在第一个 Robocopy 之后不久启动第二个 Robocopy。
  • robocopy 是否会在使用时自动返回终端?这可能会诱使您的程序认为操作已经完成,而实际上它还没有完成
  • 不太清楚你所说的“归还终端”是什么意思。

标签: python subprocess call popen robocopy


【解决方案1】:

我使用以下函数来启动我的命令,该命令一直等到操作完成,但有一个超时:

import os
import logging

logger = logging.getLogger()

def command_runner(command, valid_exit_codes=None, timeout=30, shell=False, decoder='utf-8'):
    """
    command_runner 2019103101
    Whenever we can, we need to avoid shell=True in order to preseve better security
    Runs system command, returns exit code and stdout/stderr output, and logs output on error
    valid_exit_codes is a list of codes that don't trigger an error
    """

    try:
        # universal_newlines=True makes netstat command fail under windows
        # timeout does not work under Python 2.7 with subprocess32 < 3.5
        # decoder may be unicode_escape for dos commands or utf-8 for powershell
        if sys.version_info >= (3, 0):
            output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=shell,
                                             timeout=timeout, universal_newlines=False)
        else:
            output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=shell,
                                             universal_newlines=False)
        output = output.decode(decoder, errors='backslashreplace')
    except subprocess.CalledProcessError as exc:
        exit_code = exc.returncode
        # noinspection PyBroadException
        try:
            output = exc.output
            try:
                output = output.decode(decoder, errors='backslashreplace')
            except Exception as subexc:
                logger.debug(subexc, exc_info=True)
                logger.debug('Cannot properly decode error. Text is %s' % output)
        except Exception:
            output = "command_runner: Could not obtain output from command."
        if exit_code in valid_exit_codes if valid_exit_codes is not None else [0]:
            logger.debug('Command [%s] returned with exit code [%s]. Command output was:' % (command, exit_code))
            if output:
                logger.debug(output)
            return exc.returncode, output
        else:
            logger.error('Command [%s] failed with exit code [%s]. Command output was:' %
                         (command, exc.returncode))
            logger.error(output)
            return exc.returncode, output
    # OSError if not a valid executable
    except OSError as exc:
        logger.error('Command [%s] faild because of OS [%s].' % (command, exc))
        return None, exc
    except subprocess.TimeoutExpired:
        logger.error('Timeout [%s seconds] expired for command [%s] execution.' % (timeout, command))
        return None, 'Timeout of %s seconds expired.' % timeout
    except Exception as exc:
        logger.error('Command [%s] failed for unknown reasons [%s].' % (command, exc))
        logger.debug('Error:', exc_info=True)
        return None, exc
    else:
        logger.debug('Command [%s] returned with exit code [0]. Command output was:' % command)
        if output:
            logger.debug(output)
        return 0, output


# YOUR CODE HERE

executable = os.path.join(os.environ['SYSTEMROOT'], 'system32', 'robocopy.exe')
mycommand = '"%s" "%s" "%s" "%s"' % (executable, source, dest, options)
result, output = command_runner(mycommand, shell=True)

【讨论】:

  • 我不太明白代码。其中哪一部分确保 robocopy 将在第二个 robocopy 开始之前完成运行?
  • 这是 subprocess.check_output 等待命令完成,除非达到超时。
【解决方案2】:

我发现了什么:感谢 QuantumChris。我发现 robocopy 从终端返回并返回到我的脚本中,尽管我使用了 subprocess.run,它应该暂停我的脚本,直到它完成运行。在进行第二个 robocopy 之前,我通过检查文件是否已复制到目标文件夹来阻止第二个 robocopy 运行。问题是如果最后一个文件很大,那么 os.path.isfile() 会检测到文件 WHILE 仍在被复制。因此它使用了第二个 robocopy,但是第二个 robocopy 没有检测到最后一个文件,因此尝试复制文件,但认识到它无法访问该文件,因为它已经在使用(由第一个 robocopy 使用)所以它等待30 秒后再试一次。 30 秒后,它可以访问文件并将其复制过来。我现在想做的是让我的最后一个文件成为一个空的虚拟文件,我不关心它被复制两次,因为它是空的。 Robocopy 似乎按照 ASCII 顺序复制文件。所以我把文件命名为 ~~~~~.txt :D

【讨论】:

    【解决方案3】:

    试试:

    while temp2.poll() is not None:
        # ... do something else, sleep, etc
    
    out, err = temp2.communicate()
    

    【讨论】:

      猜你喜欢
      • 2019-02-03
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 2014-12-17
      • 2017-09-07
      • 1970-01-01
      相关资源
      最近更新 更多