Python子进程、mysqldump和管道

2024-09-28 21:51:55 发布

您现在位置:Python中文网/ 问答频道 /正文

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

错误在使用子进程的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=yyyyyyyyy数据库名称| gzip>;/home/drush backup/2010-08-30.15.37/db/数据库名称.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文档中看到了这个示例:

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

显然,如果我在终端上尝试这个命令,它就会工作。


Tags: pathdbsqlstdoutpassworddatabasebackupsubprocess
3条回答

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

管道(和重定向)被识别为这样,并由shell调度,并且在默认情况下(在Unix上),subprocess避免使用shell(它比较慢,给您的控制力也比较小)——如果管道或重定向是您绝对必须拥有的,则需要显式强制shell处于控制状态。

通常情况下,人们会尽量在Python中避免管道(从而避免shell=True和关注的问题)(例如,在您的例子中,使用Python标准库的gzip模块)。当然,对于这个,必须小心地将stdout(将进一步处理)与stderr分离,作为两个独立的管道。

我不知道这管子怎么解释。如果这是一个问题,您可以编程创建一个pipelline。

发件人: 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 and stderr specify the executed programs’ standard input, standard output and standard error file handles, respectively. Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None.

示例:

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()

给定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)

subprocess.Popen()中使用f作为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))

相关问题 更多 >