2010-08-30 71 views
2

我試圖構建一個簡單的備份/升級數據庫腳本時遇到了問題。Python子流程,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) 

是COSE之前,我重定向sys.stdoutsys.stderr的文件,纔能有一個日誌系統。

在這些日誌中,我發現錯誤:

[mysqldump的--user = XXXXXX --password = YYYYYYYY數據庫名稱| 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文檔:

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

的逗號添加一個空格,但加號不。 domaindb和管道連接在一起。也許這是問題?不知道你爲什麼要用逗號連接字符串,而不是僅僅使用空格並將它們保留在相同的引號內。 – xnine 2010-08-30 13:57:49

+0

在python中可能是新手了)無論如何,我認爲我必須使用+,因爲'--user ='和db_user之間不能有空格,正確的形式應該是'--user = foo',或者我誤會了? – Strae 2010-08-30 14:12:50

回答

3

我不確定管道將如何解釋。如果這是一個問題,你可以編程創建一個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 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() 
+0

我剛剛做了並編輯了我的問題;現在它打破了'>'步驟 – Strae 2010-08-30 14:17:59

+0

@DaNieL - 我已經更新了我的答案 – 2010-08-30 15:03:26

2

嘗試subprocess.Popen(' '.join(cmdL), shell=True)

管道(和重定向)被認爲是這樣的,並由外殼計劃,並在默認情況下(在UNIX上),subprocess避免使用shell(它的速度較慢,爲您提供了較少的控制) - 你需要明確如果一個管道或重定向是你絕對必須擁有的,那麼迫使一個shell被控制。

正常情況下,通過儘可能多地使用Python(例如,在您的情況下,使用Python標準庫的gzip模塊)嘗試避免管道(因此避免shell=True以及出席和問題)。當然,這個人必須將stdout(需要進一步處理)從stderr中分離出來,作爲兩個獨立的管道。

2

有了路徑,用戶,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()也適用,但不壓縮數據。 在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))