Python子进程,mysqldump和管道

时间:2010-08-30 13:51:23

标签: python subprocess mysqldump

我在尝试构建简单的备份/升级数据库脚本时遇到了问题。

错误发生在使用子进程的mysqldump调用中:

cmdL = ["mysqldump", "--user=" + db_user, "--password=" + db_pass, domaindb + "|", "gzip", ">", databases_path + "/" + domaindb + ".sql.gz"]
print "%s: backup database %s \n\t[%s]" % (domain, domaindb, ' '.join(cmdL))
total_log.write("%s: backup database %s \n\t[%s] \n" % (domain, domaindb, ' '.join(cmdL)))
p = subprocess.Popen(cmdL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

在此之前,我将sys.stdoutsys.stderr重定向到文件,以便拥有日志系统。

在那些日志中,我发现错误:

[mysqldump --user = xxxxxx --password = yyyyyyyy database_name | gzip> /home/drush-backup/2010-08-30.15.37/db/database_name.sql] [错误]:mysqldump:找不到表:“|”

似乎|字符被视为mysqldump参数,而不是管道。

查看python子流程文档,这是正常的,但我怎样才能获得我需要的东西(调用命令mysqldump --user=xxxxxx --password=yyyyyyyy database_name | gzip > /home/drush-backup/2010-08-30.15.37/db/database_name.sql)?

编辑我只是在python docs上看到这个例子:

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

我编辑了我的脚本:

command = ["mysqldump", "--user=" + db_user, "--password=" + db_pass, domaindb, "|", "gzip", ">", databases_path + "/" + domaindb + ".sql.gz"]
cmdL1 = ["mysqldump", "--user=" + db_user, "--password=" + db_pass, domaindb]
cmdL2 = ["gzip", ">", databases_path + "/" + domaindb + ".sql.gz"]

print "%s: backup database %s \n\t[%s]" % (domain, domaindb, ' '.join(command))
total_log.write("%s: backup database %s \n\t[%s] \n" % (domain, domaindb, ' '.join(command)))

p1 = subprocess.Popen(cmdL1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p2 = subprocess.Popen(cmdL2, stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
cmdError, cmdData = p2.communicate()

现在命令变量仅用于日志中。

这是下一步,但它会在>信息流中停止,并显示以下错误:

[Error]: gzip: >: No such file or directory
gzip: /path/to/backups/dir/natabase_name.sql.gz: No such file or directory

显然,如果我在终端中尝试该命令,它可以工作。

3 个答案:

答案 0 :(得分:4)

我不确定如何解释管道。如果这是一个问题,您可以以编程方式创建一个pipelilne。

自: http://docs.python.org/library/subprocess.html#replacing-shell-pipeline

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

修改

对于文件重定向,您可以将stdout定向到文件..

  stdin,stdout和stderr指定了   执行程序的标准输入,   标准输出和标准错误   文件句柄。有效   值是PIPE,一个现有文件   描述符(正整数),an   现有文件对象,无。

示例:

out_file = open(out_filename, "wb")
gzip_proc = subprocess.Popen("gzip", stdout=out_file)
gzip_proc.communicate()

或者如果您接受Alex的建议并使用Python的标准库gzip模块,您可以这样做:

import gzip
import subprocess

...
#out_filename = path to gzip file

cmdL1 = ["mysqldump", "--user=" + db_user, "--password=" + db_pass, domaindb]
p1 = subprocess.Popen(cmdL1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
dump_output = p1.communicate()[0]

f = gzip.open(out_filename, "wb")
f.write(dump_output)
f.close()

答案 1 :(得分:3)

给出path,user,pswd和dbname,以下内容就像魅力一样:

import gzip
from subprocess import Popen, PIPE

cmd = "mysqldump --user={user} --password={pswd} {dbname}".format(**locals())        
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
with gzip.open(path, "wb") as f:
    f.writelines(p.stdout)

f中使用subprocess.Popen()作为stdout参数也可以,但不会压缩数据。 在Python 2.7之前,with语句不起作用,因此请使用f=gzip.open(..)f.close()。可以使用p.stderr.read()读取错误,因此如果这不是空字符串,则最好引发异常

<小时/> 要恢复备份,您可以执行以下操作:

cmd = "mysql --user={user} --password={pswd} {dbname}".format(**locals())
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
with gzip.open(path, "rb") as f:
    p.stdin.write(f.read())
    p.communicate()[0]
    p.stdin.close()
    p_err = p.stderr.read()
if p_err:
    raise Exception('Error restoring database:\n{0}'.format(p_err))

答案 2 :(得分:2)

尝试subprocess.Popen(' '.join(cmdL), shell=True)

管道(和重定向)被识别为 shell ,并且默认情况下(在Unix上),subprocess避免使用shell(它更慢并且给你的更少)控制) - 如果管道或重定向是您绝对必须拥有的,则需要显式强制shell处于控制之中。

通常情况下,人们试图通过在Python中尽可能多地尽可能多地避免管道(因此避免shell=True和参与问题)(例如,在您的情况下,使用Python的gzip模块标准库)。当然,对于这个,必须小心地将stdout(将要进一步处理)与stderr分开,作为两个单独的管道。