我在尝试构建简单的备份/升级数据库脚本时遇到了问题。
错误发生在使用子进程的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.stdout
和sys.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
显然,如果我在终端中尝试该命令,它可以工作。
答案 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分开,作为两个单独的管道。