2010-08-30 8 views
2

J'ai un problème pour essayer de créer un script de base de données de sauvegarde/mise à niveau facile.Sous-processus Python, mysqldump et tuyaux

L'erreur est dans l'appel à l'aide mysqldump subprocess:

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) 

Avant que cose, rediriger sys.stdout et sys.stderr aux fichiers, afin d'avoir un système de journal.

Dans ces journal, je trouve l'erreur:

[mysqldump --user = xxxxxx --password = YYYYYYYY bdd | gzip> /home/drush-backup/2010-08-30.15.37/db/database_name.sql] [Erreur]: mysqldump: Impossible de trouver la table: "|"

Semblez que le caractère | est vu comme un argument mysqldump, à la place comme un tuyau. En regardant la documentation du sous-processus python, c'est normal, mais comment puis-je obtenir ce dont j'ai besoin (appelez la commande mysqldump --user=xxxxxx --password=yyyyyyyy database_name | gzip > /home/drush-backup/2010-08-30.15.37/db/database_name.sql)?

EDIT Je viens de voir cet exemple sur docs python:

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

et j'ai modifier mon script:

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

maintenant la variable de commande est simplement utilisée pour plus de commodité dans les journaux.

Cette aller une étape suivante, mais il arrête le flux >, avec cette erreur:

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

Il est évident que, si je tente la commande dans un terminal cela fonctionne.

+0

Les virgules ajouter un espace, mais le signe plus ne fait pas. domaindb et le tuyau sont joints avec un plus. Peut-être que c'est le problème? Vous ne savez pas pourquoi vous joignez des chaînes avec des virgules plutôt que d'utiliser simplement des espaces et de les conserver dans les mêmes guillemets. – xnine

+0

Soit im newbie en python;) de toute façon, je pense que je dois utiliser le +, car il ne doit pas y avoir d'espace entre '--user =' et l'utilisateur db .. la forme correcte devrait être '--user = foo', ou est-ce que je me trompe? – Strae

Répondre

3

Je ne suis pas sûr de comment le tuyau sera interprété. Si c'est un problème, vous pouvez créer un Pipelilne par programmation.

de: 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] 

modifier

En ce qui concerne la redirection de fichiers, vous pouvez diriger stdout vers un fichier ..

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.

Exemple:

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

ou si vous prenez les conseils d'Alex et utiliser le module bibliothèque gzip standard de Python, vous pouvez faire quelque chose comme ceci:

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

je viens de faire et édité ma question; maintenant il a rompu avec l'étape '>' – Strae

+0

@DaNieL - J'ai mis à jour ma réponse –

2

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

Pipelines (et redirections) sont reconnus comme tels et planifiées par le shell et, par défaut (sous Unix), subprocess évite d'utiliser une coque (il est plus lent et vous donne moins de contrôle) - vous devez explicitement forcer un shell à être en contrôle, si un pipeline ou une redirection est ce que vous devez absolument avoir.

Normalement, on essaie d'éviter les pipelines (et donc éviter shell=True et les problèmes de attendand) en faisant autant que possible en Python (par exemple, dans votre cas, avec le module gzip de bibliothèque standard de Python). Bien sûr, pour cela, il faut soigneusement séparer stdout (qui doit être traité plus avant) de stderr, comme deux pipes séparées.

2

Avec chemin, l'utilisateur, pswd et dbname donné, les œuvres suivantes comme un charme:

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) 

en utilisant f comme argument stdout dans subprocess.Popen() fonctionne également mais ne compresse pas les données. Avant Python 2.7, l'instruction with ne fonctionne pas, utilisez f=gzip.open(..) et f.close(). Les erreurs peuvent être lus avec p.stderr.read(), donc si ce n'est pas une chaîne vide, vous feriez mieux de soulever une exception


Pour restaurer la sauvegarde vous pouvez effectuer les opérations suivantes:

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