0

我正在编写一个脚本来自动化 MySQL 中的数据库导入。我正在尝试编写在pv导入数据库时​​显示输出的代码:

    pv = subprocess.Popen(
        ["pv", "-f", restore_filepath],
        bufsize=1,
        stderr=subprocess.PIPE,
        stdout=subprocess.PIPE,
        universal_newlines=True,
        shell=True,
    )
    subprocess.Popen(
        [
            "mysql",
            "-u{}".format(db_user),
            "-p{}".format(db_pass),
            db_name,
        ],
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.DEVNULL,
        stdin=pv.stdout,
    )
    for line in pv.stderr:
        print("hello")
        sys.stdout.write("\r" + line.rstrip("\n"))
        sys.stdout.flush()

这是基于this question中的代码,但它不适用于我。hello即使我注释掉 for 循环中的其他行,也从未打印过 -是for line in pv.stderr阻塞的,我不知道为什么。它永远不会解除阻塞,所以即使进程完成,程序仍然卡住 - 我必须杀死它。

我做错了什么导致for line in pv.stderr阻塞?

4

1 回答 1

2

当 shell=True时,args[0]是要执行的命令并且args[1:]是传递给sh. 你想-prestore_filepath被传递给pvsh。所以使用shell=False. 你的另一个subprocess.Popen电话也是如此。

由于 with shell=True,pv没有接收任何参数,所以它挂起,因为它仍在等待文件名。同样,解决方案是使用shell=False.

另请注意,在更换 shell 管道时,如果存在第二个进程,则应关闭stdout第一个进程以允许其接收 SIGPIPE。这允许一些程序(那些处理 SIGPIPE 的程序)在管道中断时更优雅地退出。

pv = subprocess.Popen(
    ["pv", "-f", restore_filepath],
    shell=False,  # optional, since this is the default
    bufsize=1,
    stderr=subprocess.PIPE,
    stdout=subprocess.PIPE,
    universal_newlines=True,
)
mysql = subprocess.Popen(
    ["mysql",
     "-u{}".format(db_user),
     "-p{}".format(db_pass),
     db_name],
    shell=False,
    stdout=subprocess.PIPE,
    stderr=subprocess.DEVNULL,
    stdin=pv.stdout,
)
pv.stdout.close() # Allow pv to receive a SIGPIPE if mysql exits. 
for line in pv.stderr:
    print("hello")
    sys.stdout.write("\r" + line.rstrip("\n"))
    sys.stdout.flush()
于 2018-12-30T22:46:59.713 回答