Jörg: Datenbank aus Shellscript-Sicherung wiederherstellen

0 44

Datenbank aus Shellscript-Sicherung wiederherstellen

Jörg
  • bash-script
  • datensicherung
  • php
  1. 0
    Raketenwilli
    1. 0
      Jörg
      1. 0
        Raketenwilli
        1. 0
          Jörg
          1. 0
            Raketenwilli
            1. 0
              Jörg
              1. 0
                Raketenwilli
                1. 0
                  Jörg
                2. 0

                  Datenbank aus Shellscript-Sicherung wiederherstellen: Fehler gefunden?

                  Jörg
                  1. 0
                    Jörg
                    1. 0
                      Raketenwilli
                      1. 0
                        Jörg
                        1. 0
                          Jörg
                          1. 0
                            Raketenwilli
                        2. 0
                          Raketenwilli
                          1. 0
                            Jörg
                            1. 0
                              Raketenwilli
                              1. 0
                                Jörg
                                1. 0
                                  Raketenwilli
                                  1. 0
                                    Jörg
                  2. 0
                    Raketenwilli
                    1. 0
                      Jörg
                      1. 0
                        Raketenwilli
                        1. 0
                          Jörg
                          1. 0
                            Raketenwilli
                            1. 0
                              Jörg
                              1. 0
                                Raketenwilli
                                1. 0
                                  Jörg
                                  1. 0
                                    Raketenwilli
                                    1. 0
                                      Jörg
                                      1. 0
                                        Raketenwilli
                                        1. 0
                                          Jörg
                                          1. 0
                                            Der Martin
                                          2. 0

                                            Kastensatz des Programmierers

                                            Raketenwilli
                                            1. 0
                                              Jörg
                                              1. 0
                                                Raketenwilli
                                    2. 0
                                      Rolf B
                                      1. 0

                                        Ja. Aber ...

                                        Raketenwilli
                                        1. 0
                                          Rolf B
                                          1. 0
                                            Raketenwilli
                                        2. 0

                                          Fortsetzung

                                          Raketenwilli
  2. 0
    Der Martin
    • datensicherung
    • meinung
    • programmiertechnik
    1. 0
      Jörg

Hallo,

ich nutze ein wunderbares Script von Raketenwilli (danke dafür nochmal!), um nachts meine Datenbanken zu sichern.

Nun wollte ich mal testhalber hiervon eine db wiederherstellen, kriegs aber nicht hin.

So hatte ich es versucht:

$db_host = 'localhost';
$db_user = 'user'; // User der zu rettenden DB
$db_pass = 'pass'; // Passwort der zu rettenden DB
$db_name = 'mydb'; // Name der zu rettenden DB
$wp_path = 'my/path/to/dbxyz.sql.gz'; // die genaue Backupdatei (der sql-dump)


// Erstmal alle Tabellen löschen
$mysqli = new mysqli($db_host,$db_user,$db_pass,$db_name);
if($mysqli->connect_error) {
    die('Connect Error ('.$mysqli->connect_errno.') '
        .$mysqli->connect_error);
}
$result = $mysqli->query("show tables"); // run the query and assign the result to $result
while($table = $result->fetch_array()) { // go through each row that was returned in $result
    //    echo($table[0] . "<BR>");
    $mysqli->query('DROP TABLE '.$table[0]);
    //    echo "geloescht <br>"; // print the table that was returned on that row.
}


system('/bin/gunzip -c '.$wp_path.' | /usr/bin/mysql -u'.$db_user.' -p'.escapeshellarg($db_pass).' -h'.$db_host.' '.$db_name.' ',$fp);
if($fp == 0) {
    echo "Daten importiert";
} else {
    echo "Es ist ein Fehler aufgetreten";
}

Fehlermeldung:

0 0 Es ist ein Fehler aufgetreten 

Was mache ich falsch?
Bzw., wie kann ich die db aus dem dump wiederherstellen?

Über php oder per Shellscript ist eigentlich nebensächlich, wobei mir ein Shellscript fast lieber wäre, weil je nach Größe der wiederherzustellenden db das php-script abbrechen könnte.

Jörg

  1. Die Idee, einen Systembefehl als Einzeiler zu erzeugen und loszuballern erscheint „schlank“, hat aber Nachteile bei der Fehlersuche.

    Rezept:

    1. Trenne das!
    2. Arbeite bei Shell-Befehlen mit einer Umleitung, um Fehler zeigen zu können.
    3. Notiere übersichtlicher! (Lange Zeilen sind Mist!)
    $sys='/bin/gunzip -c ' . $wp_path . ' 2>> /tmp/error.log'
         . ' | /usr/bin/mysql -u' . $db_user 
         . ' -p' . escapeshellarg( $db_pass ) 
         . ' -h' . $db_host . ' ' . $db_name
         . ' 2>> /tmp/error.log'
    ;
    
    system( $sys , $fp );
    

    Dann kannst Du nämlich mit ...

    if( $fp == 0 ) {
        echo "Daten importiert";
    } else {
        error_reporting( E_ALL );
        ini_set( 'display_errors', 0 );
        trigger_error( 
            "Fehler beim Shell-Befehl '$sys' - Weitere Informationen in Datei /tmp/error.log!",
            E_USER_ERROR
        );
    }
    

    ... das Wegloggen des womöglich fehlerhaften Strings in das Error-Log (unter Vermeidung einer Anzeige von Datenbank, Benutzernamen und Passwort im Browser) erzwingen.

    Die Fehler, welche gzip bzw. sodann die Datenbank schmeißt, findest Du, dank der Umleitungen in der Datei /tmp/error.log. Freilich könnte man die (vor dem Ausstieg mit trigger_error(…, E_USER_ERROR)) auch ins error-log von php übernehmen…

    1. Hallo Willi,

      Rezept:

      1. Trenne das!
      2. Arbeite bei Shell-Befehlen mit einer Umleitung, um Fehler zeigen zu können.
      3. Notiere übersichtlicher! (Lange Zeilen sind Mist!)

      Das habe ich jetzt 1:1 so umgesetzt. Der Fehler wird dann auch im php-Log so aufgeführt, aber weder wird im Browserfenster was notiert, noch wird die error.log erstellt. (Ich habe übrigens extra denselben Pfad für die error.log genommen, in dem auch schon der sql-dump liegt, um zu vermeiden, dass der error.log irgendwohin geschrieben wird und ich ihn einfach nur nicht finde. Habe daher wirklich absolute Pfade, ausgehend von meinem root-Verzeichnis genommen, das ist immer das Sicherste).

      Aber kein error.log weit und breit.

      Was läuft falsch?

      Jörg

      1. Das habe ich jetzt 1:1 so umgesetzt.

        Die Erfahrung sagt, dass unter „1:1“ manchmal ziemlich viel verstanden wird… An manchen Stellen hier sind schon Leerzeichen ziemlich entscheidend.

        Zeigst Du Deine 1:1-Umsetzung?

        Was läuft falsch?

        Zu erst einmal gibst Du bitte $sys aus, kopierst das und versuchst das im Terminal auszuführen. Und/oder zeigst es hier. Lösch die geheimen Daten vorher heraus.

        Und wenn Du schon mal das Terminal offen hast versuchst Du:

        $ordner="/dein/Verzeichnis"
        ls -ld "$ordner";
        ls -l  "$ordner";
        

        gut möglich, dass „was mit den Rechten nicht stimmt“. Zeig auch das.

        Aus dem Grund hatte ich übrigens das Verzeichnis für das error.log auf /tmp/ gesetzt…

        1. Zu erst einmal gibst Du bitte $sys aus, kopierst das und versuchst das im Terminal auszuführen. Und/oder zeigst es hier. Lösch die geheimen Daten vorher heraus.

          Habe ich gemacht.
          Erhalte Folgendes:

          0
          0
          

          Und es wird diesmal ein Errorlog genau im Verzeichnis angelegt, das ich hierfür vorgesehen hatte, aber drin steht nur:

          mysql: [Warning] Using a password on the command line interface can be insecure.
          

          Und wenn Du schon mal das Terminal offen hast versuchst Du:

          $ordner="/dein/Verzeichnis"
          ls -ld "$ordner";
          ls -l  "$ordner";
          
          #ls -ld "$ordner";
          drwxr-x--- 2 user1 user2 116 Aug 29 14:55 .
          
          #ls -l  "$ordner";
          -rw-rw-rw- 1 user1 user2 3562254 Aug 29 14:54 dump1.sql.gz
          -rw-rw-rw- 1 user1 user2 3334237 Aug 29 14:53 dump2.sql.gz
          -rwxr-xr-x 1 user1 user2 1671 Aug 29 14:54 dump_import.php
          -rw-r----- 1 user1 user2 81 Aug 29 15:00 error.log
          

          Jörg

          1. $sys='/bin/gunzip -c ' . $wp_path . ' 2>> /tmp/error.log'
                 . ' | /usr/bin/mysql -u' . $db_user 
                 . ' -p' . escapeshellarg( $db_pass ) 
                 . ' -h' . $db_host . ' ' . $db_name
                 . ' 2>> /tmp/error.log'
            ;
            
            system( $sys , $fp );
            

            Dieses $sys sollst Du ausgeben. Da müsste also der auszuführende Befehl sichtbar werden.

            Nicht (wie von Dir gezeigt) das Ergebnis von

            system( $sys );
            

            Die Datei error.log hat 81 Bytes. Was steht denn drin?

            1. Dieses $sys sollst Du ausgeben. Da müsste also der auszuführende Befehl sichtbar werden.

              Ok. Da scheint in der error-log-Dateianbindung ein Fehler drin zu sein:

              Funktioniert:

              /bin/gunzip -c /my/pfad/myDB.sql.gz | /usr/bin/mysql -udb15846678-myDB -p'myPass' -hlocalhost db15846678-myDB
              

              Funktioniert nicht:

              /bin/gunzip -c /my/pfad/myDB.sql.gz 2>> /my/pfad/error.log | /usr/bin/mysql -udb15846678-myDB -p'myPass' -hlocalhost db15846678-myDB 2>> /my/pfad/error.log
              

              Nicht (wie von Dir gezeigt) das Ergebnis von

              system( $sys );
              

              Die Datei error.log hat 81 Bytes. Was steht denn drin?

              mysql: [Warning] Using a password on the command line interface can be insecure.
              

              Jörg

              1. Dieses $sys sollst Du ausgeben. Da müsste also der auszuführende Befehl sichtbar werden.

                Ok. Da scheint in der error-log-Dateianbindung ein Fehler drin zu sein:

                Funktioniert:

                /bin/gunzip -c /my/pfad/myDB.sql.gz | /usr/bin/mysql -udb15846678-myDB -p'myPass' -hlocalhost db15846678-myDB
                

                Funktioniert nicht:

                /bin/gunzip -c /my/pfad/myDB.sql.gz 2>> /my/pfad/error.log | /usr/bin/mysql -udb15846678-myDB -p'myPass' -hlocalhost db15846678-myDB 2>> /my/pfad/error.log
                

                Hm.

                1. Das, was bei Dir „nicht funktioniert“ funktioniert bei mir bestens.
                2. Bedeutet Dein „Funktioniert“, dass die Datentabellen eingelesen wurden?

                Wenn nicht liegt der Fehler außerhalb des Gezeigten. Entpacke mal die /my/pfad/myDB.sql.gz „manuell“ und lese darin nach, was da für Befehle drin stehen. Aber eigentlich sollte der mysql- oder mariadb-Client dann Fehlermeldungen anzeigen.

                1. Hm.

                  1. Das, was bei Dir „nicht funktioniert“ funktioniert bei mir bestens.

                  Oh...

                  1. Bedeutet Dein „Funktioniert“, dass die Datentabellen eingelesen wurden?

                  Ja, genau. Der funktionierende befehl importiert die db tadellos, der nicht funktionierende importiert nichts und schreibt auch nicht in den errorlog.

                  Wenn nicht liegt der Fehler außerhalb des Gezeigten. Entpacke mal die /my/pfad/myDB.sql.gz „manuell“ und lese darin nach, was da für Befehle drin stehen. Aber eigentlich sollte der mysql- oder mariadb-Client dann Fehlermeldungen anzeigen.

                  Hab ich auch schon gemacht. Und deshalb auch schonmal die sql.gz gewechselt. Aber an der scheint es nicht zu liegen.

                2. Hallo Willi,

                  Wenn nicht liegt der Fehler außerhalb des Gezeigten. Entpacke mal die /my/pfad/myDB.sql.gz „manuell“ und lese darin nach, was da für Befehle drin stehen. Aber eigentlich sollte der mysql- oder mariadb-Client dann Fehlermeldungen anzeigen.

                  Nach gefühlt tausend Tests habe ich nun einen Parameter beim Erstellen der Backupdaten ausgemacht, der den erfolgreichen Import mit dem von mir gezeigten Code stört. (Das erklärt freilich nihct, warum Dein Import-Code bei mir nicht funktioniert, aber ich komme zumindest weiter).

                  $mysqldump_bin --host="${host}" --port="${port}" --user="${user}" --password="${passwort}" $moreDumpOptions -B $database | gzip -c > "${outFile}" 2>> "${logFile}" ;
                  

                  In dieser Zeile stört der Parameter -B. Mit dem Parameter funktioniert mein Import

                  system('/bin/gunzip -c ' .$wp_path. ' | /usr/bin/mysql -u' .$db_user. ' -p' .escapeshellarg($db_pass). ' -h' .$db_host. ' ' .$db_name. ' ', $fp);
                  

                  nicht.
                  Wenn ich den Parameter -B hingegen weglasse, dann funktioniert der Import der so erstellten Datei mit obiger Zeile.

                  Der Unterschied im Dump ist, dass eine Zeile USE myDb; hinzugefügt wird. Das scheint den Import (zumindest mit meinem Code) zuverlässig zu verhindern.

                  Woran liegt das?

                  Gruß, Jörg

                  1. Der Unterschied im Dump ist, dass eine Zeile USE myDb; hinzugefügt wird. Das scheint den Import (zumindest mit meinem Code) zuverlässig zu verhindern.

                    Woran liegt das?

                    Wenn ich also nun einen mit der Option -B erzeugten Dump mit mienem Importcode einlesen möchte, kann ich diese Anweisung USE myDB; aus dem Dump löschen, die Datei danach nochmal durch gzip laufen lassen und sie dann importieren.

                    Frage: Gibts auch eine einfachere Möglichkeit?

                    Und: Woran liegt das technisch gesehen?

                    Jörg

                    1. Wenn ich also nun einen mit der Option -B erzeugten Dump mit mienem Importcode einlesen möchte, kann ich diese Anweisung USE myDB; aus dem Dump löschen, die Datei danach nochmal durch gzip laufen lassen und sie dann importieren.

                      Frage: Gibts auch eine einfachere Möglichkeit?

                      grep kann sowas:

                      $> gunzip -c backup.sql.gz | grep -vP "^USE myDB;$" | mysql …
                      

                      Das filtert besagte Zeilen vor der Weiterverarbeitung aus, ändert aber die Datei mit dem wertvollen Backup aber gerade nicht. Das musst Du aber anpassen und testen:

                      $> gunzip -c backup.sql.gz | head -n 30 | grep -P "^USE myDB;$" | less
                      

                      Packt aus, bricht nach 30 Zeilen ab, sucht, zeigt in less sodann die Zeilen an, die GENAU eine Zeile USE myDB; enthalten.

                      Wird nichts ausgegeben, erhöhe die Zeilenzahl oder sehe nach was grep filtern soll (regex hinter '-P'). Wenn die Zeilenzahl erhöht werden muss, dann mach das auch im „Gegentest“:

                      $> gunzip -c backup.sql.gz | head -n 30 | grep -vP "^USE myDB;$" | less
                      

                      Packt aus, bricht nach 30 Zeilen ab, sucht, zeigt in less sodann jene Zeilen an, welche die GENAUE Zeile USE myDB; NICHT enthalten.

                      Wenn die Ergebnisse passen, dann passe den Befehl (oben, unter „grep kann sowas:“) an.

                      1. Hallo Willi,

                        grep kann sowas:

                        fein. 😀

                        Nur, wozu soll ich die genaue Zeile raussuchen, ich weiß doch, dass besagte Anweisung in Zeile 31 steht.
                        Habe das jetzt dennoch mit Deiner Methode gegengeprüft, aber das wußte ich auch vorher schon, weil ich ja die entpackte Datei (den Dump) kenne.

                        Noch nicht ganz verstanden habe ich, wie ich folgende Zeile mit diesem Wissen anpassen muss.

                        $> gunzip -c backup.sql.gz | grep -vP "^USE myDB;$" | mysql …
                        

                        Die ganze Datei durchlaufen lassen, oder kann ich das trennen in gefilterten Text und ungefilterten?
                        Das verstehe ich noch nicht ganz.

                        Jörg

                        1. $> gunzip -c backup.sql.gz | grep -vP "^USE myDB;$" | mysql …
                          

                          Die ganze Datei durchlaufen lassen, oder kann ich das trennen in gefilterten Text und ungefilterten?
                          Das verstehe ich noch nicht ganz.

                          Habe über die Shell nunmal einfach

                          /bin/gunzip -c /my/path/myDump.sql.gz | grep -vP "^USE \`myDB\`;$" | /usr/bin/mysql -umyUser -p'myPass' -hlocalhost myDBname
                          

                          durchlaufen lassen, das hat prima funktioniert.

                          Ich weiß aber nicht, ob Du das so gemeint hast, weil ich ja head -n hier gar nicht genutzt habe und meine db recht klein ist (was dann ggf. relevant ist).

                          Ich vermute viel eher, dass Du event. gemeint hattest, dass man den zu ersetzenden Teil der Datei eingrenzen kann und den Rest ungefiltert durchschickt?

                          1. $> gunzip -c backup.sql.gz | grep -vP "^USE myDB;$" | mysql …
                            

                            Die ganze Datei durchlaufen lassen, oder kann ich das trennen in gefilterten Text und ungefilterten?
                            Das verstehe ich noch nicht ganz.

                            Habe über die Shell nunmal einfach

                            /bin/gunzip -c /my/path/myDump.sql.gz | grep -vP "^USE \`myDB\`;$" | /usr/bin/mysql -umyUser -p'myPass' -hlocalhost myDBname
                            

                            durchlaufen lassen, das hat prima funktioniert.

                            Ich weiß aber nicht, ob Du das so gemeint hast, weil ich ja head -n hier gar nicht genutzt habe und meine db recht klein ist (was dann ggf. relevant ist).

                            Ich vermute viel eher, dass Du event. gemeint hattest, dass man den zu ersetzenden Teil der Datei eingrenzen kann und den Rest ungefiltert durchschickt?

                            Nein. Die Zeilen mit dem Pipe durch head waren für den Test bestimmt, ob das grep, respektive der Suchausdruck auch greift. Stell Dir das wie einen Mengenbegrenzer für Flüssigkeiten in einer Chemieanlage vor: Nach der festgelegten Menge ist der Hahn zu, es kommt gar nichts mehr ins Rohr (Deshalb das | als Symbol für „pipe“), also auch nicht bei weiteren Reaktoren oder Filtern an.

                            Du solltest Dir die Resultat anschauen und dazu ist es kontraproduktiv, wenn es womöglich tausende Zeilen mit Inserts enthält. Nur dazu diente die Begrenzung auf die ersten 30 Zeilen der entpackten Datei.

                            Grundlagenwissen:

                            Beispiele:

                            • head -n 2 file.txt gibt die ersten 2 Zeilen der Datei 'file.txt' aus. Wird keine Input-Datei genannt, dann versucht head den Input aus <STRIN> zu filtern.
                            • gunzip -c file.txt.gz | head -n2 gibt also die erste beiden Zeilen der in file.txt.gz gepackten Datei aus.

                            Aufgabe:

                            Denkhilfe: „Der Zweite ist der erste Verlierer.“

                            • gunzip -c file.txt.gz | head -n 2 | tail -n1 gibt nur die zweite Zeile der in file.txt.gz gepackten Datei aus. Versuche herauszubekommen, warum das so ist. Lies die manpages, teste die Befehle schrittweise. Und denke an die Denkhilfe.
                        2. Hallo Willi,

                          grep kann sowas:

                          fein. 😀

                          Nur, wozu soll ich die genaue Zeile raussuchen, ich weiß doch, dass besagte Anweisung in Zeile 31 steht.
                          Habe das jetzt dennoch mit Deiner Methode gegengeprüft, aber das wußte ich auch vorher schon, weil ich ja die entpackte Datei (den Dump) kenne.

                          Noch nicht ganz verstanden habe ich, wie ich folgende Zeile mit diesem Wissen anpassen muss.

                          $> gunzip -c backup.sql.gz | grep -vP "^USE myDB;$" | mysql …
                          

                          Die ganze Datei durchlaufen lassen

                          Klar. Es soll ja die ganze Datei - nur eben ohne die Zeile[n] 'USE myDB;' - zur Datenbank geleitet werden. Und zwar möglichst ohne die Datei selbst zu verändern!

                          Wenn die Zeile im Dump, angenommen mit 'USE myDB;' tatsächlich 'use foo0815;' lautet, dann ändere den Aufruf in

                          gunzip -c backup.sql.gz | grep -vP "^use foo0815;$" | mysql …
                          
                          1. Hallo Willi,

                            Wenn die Zeile im Dump, angenommen mit 'USE myDB;' tatsächlich 'use foo0815;' lautet, dann ändere den Aufruf in

                            gunzip -c backup.sql.gz | grep -vP "^use foo0815;$" | mysql …
                            

                            Ah, ok.
                            Ja, perfekt, genau das hatte ich schon gemacht und funktioniert.
                            Vielen, vielen Dank für Deine Hilfe!👍

                            Ich habe das aber nun im Dumpscript auch insofern geändert, als dass ich die Option -B herausgenommen habe.
                            So kann ich dann künftig auch das grep wieder aus dem Importscript heraus nehmen.

                            Gruß, Jörg

                            1. Ich habe das aber nun im Dumpscript auch insofern geändert, als dass ich die Option -B herausgenommen habe.
                              So kann ich dann künftig auch das grep wieder aus dem Importscript heraus nehmen.

                              Beware!

                              1. Für das Backup willst Du die Option behalten.
                              2. Für die Wiederherstellung willst Du das grep natürlich aus dem Recovery-Script rauslassen.
                              3. Was Du bei sonstigen Tests (z.B. wie hier mit neuen Datenbanknamen!) tust, ist eine ganz andere Wurst.
                              1. Beware!

                                Echt?

                                1. Für das Backup willst Du die Option behalten.

                                Was bringt mir die Option -B denn?
                                Ist das dann quasi so eine Sicherheitsfunktion, die nur das Importieren in die entsprechende DB zulässt?

                                1. Für die Wiederherstellung willst Du das grep natürlich aus dem Recovery-Script rauslassen.

                                Falls ich nicht in eine andere DB sichern möchte?

                                1. Was Du bei sonstigen Tests (z.B. wie hier mit neuen Datenbanknamen!) tust, ist eine ganz andere Wurst.

                                Ja, Punkt 3 verstehe ich. 🙂

                                1. Ist das dann quasi so eine Sicherheitsfunktion, die nur das Importieren in die entsprechende DB zulässt?

                                  Wenn Du so willst… Vor allem aber lässt man beim Backup mit mysqldump eigentlich sogar die Option

                                  --no-create-db
                                  

                                  tunlichst weg und notiert statt dessen tunlichst

                                  --add-drop-database
                                  

                                  denn dann kann man (eigentlich, nur Du auf dem recht speziellen Server mit Fremdadmins und diesem „Admin-Panel“(¹) kannst es dann nicht), einfach jeden Dump hernehmen und der wird dann schon in die richtige Datenbank geschoben.

                                  ¹) Solche und andere, nicht vorhersehbare und also nicht berücksichtigbaren Umstände (und davon gibt es sehr, sehr viele) sind übrigens der Grund, warum ich angeblich „fertigen“ Backup+Recovery-Lösungen nicht über den Weg traue und das Zeug lieber selbst scripte.

                                  1. Ist das dann quasi so eine Sicherheitsfunktion, die nur das Importieren in die entsprechende DB zulässt?

                                    Wenn Du so willst… Vor allem aber lässt man beim Backup mit mysqldump eigentlich sogar die Option

                                    --no-create-db
                                    

                                    tunlichst weg und notiert statt dessen tunlichst

                                    --add-drop-database
                                    

                                    Wenn denn der Provider mitspielt, dann gerne. 😉

                                    ¹) Solche und andere, nicht vorhersehbare und also nicht berücksichtigbaren Umstände (und davon gibt es sehr, sehr viele) sind übrigens der Grund, warum ich angeblich „fertigen“ Backup+Recovery-Lösungen nicht über den Weg traue und das Zeug lieber selbst scripte.

                                    Dem schließe ich mich an.
                                    Auch wenn ich beim "selber scripten" eher nachvollziehen können muss, was Du da scriptest. 😅

                                    Ich habe die Option -B wieder in das Backupscript eingefügt und in das Importscript habe ich folgendes eingefügt.

                                    $db_host = 'localhost';
                                    $db_user = 'user'; // User der zu rettenden DB
                                    $db_pass = 'pass'; // Passwort der zu rettenden DB
                                    $db_name = 'dbtarget'; // Name der zu rettenden DB (Target)
                                    $wp_path = 'my/path/test.sql.gz'; // die genaue Backupdatei (der sql-dump)
                                    $backup_db = 'dbsource'; // Name der geretteten DB (Source)
                                    
                                    // Wenn in dieselbe db wiederhergestellt wird, dann darf USE dbblablub; im Dump stehen bleiben, ansonsten weg damit
                                    if($backup_db != $db_name) {
                                        $backupoption = ' | grep -vP ';
                                        $backupoption .= '"^USE \`';
                                        $backupoption .= $backup_db;
                                        $backupoption .= '\`;$"';
                                    } else {
                                        $backupoption = '';
                                    }
                                    
                                    // Dann Backup einspielen:
                                    $sys = '/bin/gunzip -c '.$wp_path.$backupoption.' | /usr/bin/mysql -u'.$db_user.' -p'.escapeshellarg($db_pass).' -h'.$db_host.' -B '.$db_name.' ';
                                    system($sys,$fp);
                                    

                                    Jörg

                  2. Der Unterschied im Dump ist, dass eine Zeile USE myDb; hinzugefügt wird. Das scheint den Import (zumindest mit meinem Code) zuverlässig zu verhindern.

                    Woran liegt das?

                    An Deinen Änderungen. Mein Skript hat die Datenbanken EINZELN (jede Datenbank in eine Datei) exportiert - Deine abgeleitete Version exportiert offenbar ALLE auf einmal.

                    Dabei setzt mysqldump -B halt andere Befehle in das erzeugte Skript (den „Dump“); verzichtet wohl auf das create database $NAME vor dem use $NAME;

                    Außerdem hast Du (wohl) auch die Optionen nicht übernommen.

                    1. Hi Willi,

                      Woran liegt das?

                      An Deinen Änderungen.

                      Negativ.
                      Ich habe Dein Script 1:1 übernommen.
                      Und ja, es wird jede DB einzeln in der Schleife gedumped.
                      Funktioniert prächtig, aber es wird halt in jeden Dump dieses USE db; hinein geschrieben.

                      Mein Skript hat die Datenbanken EINZELN (jede Datenbank in eine Datei) exportiert - Deine abgeleitete Version exportiert offenbar ALLE auf einmal.

                      Nein, meine Version ist Deine Version und man kann wunderbar beobachten, wie die Datenbanken einzeln gesichert werden.

                      Dabei setzt mysqldump -B halt andere Befehle in das erzeugte Skript (den „Dump“); verzichtet wohl auf das create database $NAME vor dem use $NAME;

                      -B setzt genau einen einzogen anderen befehl. USE db; Das wars schon.
                      Und auf das create database $NAME muss ich eh verzichten, weshalb ich das in den Optionen nochmal zusätzlich setze, weil mein Provider die Erzeugung von Datenbanken außerhalb des Adminpanels nicht unterstützt.

                      Außerdem hast Du (wohl) auch die Optionen nicht übernommen.

                      Doch, habe ich.

                      # siehe mysqldump --help
                      moreDumpOptions='--add-drop-database --add-drop-table --allow-keywords --extended-insert=TRUE --no-tablespaces --no-create-db'; 
                      

                      Jörg

                      1. Und auf das create database $NAME muss ich eh verzichten, weshalb ich das in den Optionen nochmal zusätzlich setze, weil mein Provider die Erzeugung von Datenbanken außerhalb des Adminpanels nicht unterstützt.

                        Tja. Das konnte ich nicht riechen. Aber vermutlich darf das der Datenbank-root (MySQL/MariaDB haben eine eigene Benutzerverwaltung). Je nach Admin-Paneel kann das aber böse Nebeneffekte haben. Manche speichern nämlich eigenes Zeug in MySQL…

                        aber es wird halt in jeden Dump dieses USE db;

                        Was mich ein wenig wundert:

                        Das sollte eigentlich so unschädlich sein wie

                        ~/tmp$ cd ~/tmp
                        

                        Es sei denn, Deine Datenbanken haben neue Namen… Analoges Beispiel:

                        ~/tmp0815$ cd ..
                        ~$ rm -rf ~/tmp0815
                        ~$ mkdir ~/tmp0816; #Im Admin-Paneel
                        ~$ cd ~/tmp0815
                        

                        geht natürlich schief.

                        --add-drop-database --no-create-db

                        Übrigens sagt schon mysqldump --help

                        -B, --databases     Dump several databases. Note the difference in usage; in
                                              this case no tables are given. All name arguments are
                                              regarded as database names. 'USE db_name;' will be
                                              included in the output.
                        
                        1. Es sei denn, Deine Datenbanken haben neue Namen… Analoges Beispiel:

                          Na klar haben sie das.
                          Ich spiele den Dump aus db1 natürlich nicht tatsächlich in db1 ein, sondern zum Testen in db2. 🤠

                          -B, --databases     Dump several databases. Note the difference in usage; in
                                                this case no tables are given. All name arguments are
                                                regarded as database names. 'USE db_name;' will be
                                                included in the output.
                          

                          Ja, habe ich dann auch entdeckt. 😮

                          1. Es sei denn, Deine Datenbanken haben neue Namen… Analoges Beispiel:

                            Na klar haben sie das.

                            „Klar“? Ernsthaft? Sowas ist gerade nicht Sinn eines Backup-Skriptes… ein solches SOLL ja gerade die Datenbanken unter ihrem alten Name wieder herstellen, damit man nicht sodann tausende Skripte mit sed durchgehen muss, um die Namen der Datenbanken anzupassen, was dann wieder viele Fehlerquellen nach sich zieht und also richtig fett Arbeit macht.

                            1. Es sei denn, Deine Datenbanken haben neue Namen… Analoges Beispiel:

                              Na klar haben sie das.

                              „Klar“? Ernsthaft?

                              Naja, im Produktivbetrieb natürlich nicht.
                              Aber zum testen? 🤔

                              1. Es sei denn, Deine Datenbanken haben neue Namen… Analoges Beispiel:

                                Na klar haben sie das.

                                „Klar“? Ernsthaft?

                                Naja, im Produktivbetrieb natürlich nicht.
                                Aber zum testen? 🤔

                                Nehm ich einen test-case, der mit dem use-case „Datenverlust“ 1:1 übereinstimmt...

                                Wozu kann ich denn bitte mit virtuellen Maschinen dealen? Also mach ich zum Testen eine Kopie der funktionierenden, diese Kopie dann mutwillig „kaputt“ - und stelle sie wieder her.

                                Auch Dein Windows hat mit dem HyperVi eine Software zum Betrieb von virtuellen Maschinen an Bord - und der hat zwar ein paar Ungereimtheiten, ist gar nicht mal so schlecht.

                                1. Hi Willi,

                                  Nehm ich einen test-case, der mit dem use-case „Datenverlust“ 1:1 übereinstimmt...

                                  Wozu kann ich denn bitte mit virtuellen Maschinen dealen? Also mach ich zum Testen eine Kopie der funktionierenden, diese Kopie dann mutwillig „kaputt“ - und stelle sie wieder her.

                                  Auch Dein Windows hat mit dem HyperVi eine Software zum Betrieb von virtuellen Maschinen an Bord - und der hat zwar ein paar Ungereimtheiten, ist gar nicht mal so schlecht.

                                  Ok, hiervon habe ich jetzt nicht wirklich etwas verstanden. 😆

                                  Edit: Ok, ich dachte, ich wäre im anderen Seitenstrang des Threads.
                                  Jetzt versteh ichs natürlich...

                                  Jörg

                                  1. statt unzähliger $var .= notiere beim nächsten Mal einfach

                                        $backupoption = ' | grep -vP '
                                                        . '"^USE \`'
                                                        . $backup_db
                                                        . '\`;$"'
                                         ;
                                    

                                    Ist „billiger“ und schneller…

                                    1. statt '.=' notiere beim nächsten Mal einfach

                                          $backupoption = ' | grep -vP '
                                                          . '"^USE \`'
                                                          . $backup_db
                                                          . '\`;$"'
                                           ;
                                      

                                      ist „billiger“ und schneller...

                                      Naja, das zusammenstückeln war eh eine Notlösung, weil ich mit den ganzen Anführungszeichen, Hochkommata und backticks ein wenig durcheinander gekommen war.

                                      1. Naja, das zusammenstückeln war eh eine Notlösung, weil ich mit den ganzen Anführungszeichen, Hochkommata und backticks ein wenig durcheinander gekommen war.

                                        Genau aus dem Grund ist es keine „Notlösung“ sondern geradezu notwendig.

                                        Stell Dir vor, Du musst das später mal wieder lesen und verstehen…

                                        1. Genau aus dem Grund ist es keine „Notlösung“ sondern geradezu notwendig.

                                          Stell Dir vor, Du musst das später mal wieder lesen und verstehen…

                                          Und der Punkt ersetzt in diesem Fall das $var .= sowie (bis auf die letzte Zeile) das Semikolon??
                                          Ich kenne diese Schreibweise so gar nicht.

                                          1. Hi,

                                            Und der Punkt ersetzt in diesem Fall das $var .= sowie (bis auf die letzte Zeile) das Semikolon??
                                            Ich kenne diese Schreibweise so gar nicht.

                                            wie, kenne ich nicht? Das ist einfach eine Anweisung, nur halt auf mehrere Zeilen gesplittet. Anstatt

                                            $foo = $a . $b . 'buh' . $c;
                                            

                                            einfach aufgeteilt:

                                            $foo = $a
                                                 . $b
                                                 . 'buh'
                                                 . $c;
                                            

                                            Merke: In PHP darf der Lesbarkeit zuliebe jederzeit Whitespace zwischen zwei Token stehen. Und Whitespace schließt auch Zeilenumbrüche mit ein.

                                            Einen schönen Tag noch
                                             Martin

                                            --
                                            Kundin zur Verkäuferin im Modegeschäft: "Sagen Sie, haben Sie auch Sachen von der Marke SALE? Die sind immer so schön günstig!"
                                          2. Ich kenne diese Schreibweise so gar nicht.

                                            Doch. Den Verkettungsoperator . kennst Du und hast ihn schon oft gesehen.

                                            • Aber vermutlich mit „alles in einer völlig notlos langen Zeile“. Bekannt als „Kastensatz des Programmierers“. Den kann ein Mensch dann tausendmal lesen ohne ihn zu verstehen.

                                            Man kann in PHP vor oder nach Operatoren und Klammern sowie dem Semikolon und Kommas (also außerhalb von Strings, Zahlen und Namen) beliebig umbrechen und einrücken - und so verständlichen Code schreiben.

                                            1. Doch. Den Verkettungsoperator . kennst Du und hast ihn schon oft gesehen.

                                              • Aber vermutlich mit „alles in einer völlig notlos langen Zeile“. Bekannt als „Kastensatz des Programmierers“. Den kann ein Mensch dann tausendmal lesen ohne ihn zu verstehen.

                                              Stimmt Beides.
                                              Aber dann frage ich mich, warum dieser Block

                                              statt '.=' notiere beim nächsten Mal einfach

                                                  $backupoption = ' | grep -vP '
                                                                  . '"^USE \`'
                                                                  . $backup_db
                                                                  . '\`;$"'
                                                   ;
                                              

                                              ist „billiger“ und schneller...

                                              nicht so geschrieben wird

                                              $backupoption = ' | grep -vP
                                                             "^USE \`'
                                                             . $backup_db
                                                             . '\`;$"'
                                                   ;
                                              

                                              Warum ein Verkettungsoperator zwischen 2 Strings?

                                              1. $backupoption = ' | grep -vP
                                                               "^USE \`'
                                                               . $backup_db
                                                               . '\`;$"'
                                                     ;
                                                

                                                Warum ein Verkettungsoperator zwischen 2 Strings?

                                                Oh. Dann hast Du aber den Zeilenumbruch im resultierenden String …

                                                 | grep -vP
                                                                  "^USE \`db0815\`;$"
                                                
                                                

                                                … den Du als Teil eines Befehls an die Shell „verfüttern“ willst.

                                                In der Shell oder Bash endet nun aber ein Befehl entweder am ; oder am Zeilenumbruch … Das ist zwar syntaktisch korrekt - nur gerade nicht das, was Du willst.

                                                By the way:

                                                … \` …
                                                

                                                Update: Alles zurück. Hier wird ein Name für SQL, Geschmacksrichtung MySQL erzeugt, der Backtick soll von der Shell aber nicht angefasst werden. Das ist also richtig.

                                    2. Hallo Raketenwilli,

                                      noch billiger und schneller wäre vermutlich

                                        $backupoption = <<<END
                                           | grep -vP "^USE \`$backup_db\`;$"';
                                          END;
                                      

                                      Das ist ein heredoc String, da kann man " und ' nach Herzenslust und ohne Escaping hineindonnern. Und Variablen auch noch.

                                      Das eingerückte END ist ab PHP 7.3 zulässig. Vorher musste es direkt am Zeilenanfang stehen. Ab PHP 7.3 bewirkt das Einrücken des END Symbols, dass die Menge an Einrückung des END Symbols auch für die Inhaltszeilen des Heredoc-Strings angenommen wird, d.h. wenn das END wie hier um 4 Spaces eingerückt ist, werden von der grep-Zeile die ersten 4 Leerstellen entfernt. Das nützt der Lesbarkeit...

                                      So, wie es oben steht, würde also genau 1 Space vor dem | stehen bleiben. Und kein einziges Linefeed, die macht Heredoc vor der ersten und nach der letzten Zeile seines Inhaltes nicht.

                                      Rolf

                                      --
                                      sumpsi - posui - obstruxi
                                      1. Hallo Raketenwilli,

                                        noch billiger und schneller wäre vermutlich

                                          $backupoption = <<<END
                                             | grep -vP "^USE \`$backup_db\`;$"';
                                            END;
                                        

                                        Das ist ein heredoc String, da kann man " und ' nach Herzenslust und ohne Escaping hineindonnern. Und Variablen auch noch.

                                        Ja. Aber …

                                        Wenn man jetzt auf die Idee kommt, dass $backup_db oder eine andere Variable mit einer Funktion „escape_fuzzi“ behandelt werden müsse, sieht der Zusammenbau dann mit der von mir vorgestellten Methode so aus:

                                        Der Änderungskandidat

                                        $backupoption = ' | grep -vP '
                                                      . '"^USE \`'
                                                      . $backup_db
                                                      . '\`;$"'
                                        ;
                                        

                                        wird ganz einfach eben mal zu:

                                        $backupoption = ' | grep -vP '
                                                      . '"^USE \`'
                                                      . escape_fuzzi( $backup_db )
                                                      . '\`;$"'
                                        ;
                                        

                                        … und auf Performance kommt es vorliegend sehr viel weniger an als auf schnelle Editierbarkeit - also Synapsenschonung und, damit verbunden, Programmierfehlerverknappung.

                                        „Perfektioniert“ wäre es:

                                        $backupoption = ' | grep -vP '
                                                        . escapeshellarg(
                                                            '^USE \`'
                                                            . $backup_db
                                                            . '\`;$'
                                                         )   
                                        ;
                                        

                                        Resultat mit $backup_db='db0815'; ist:

                                         | grep -vP '^USE \`db0815\`;$'
                                        

                                        Wobei das escapeshellarg() im konkreten, speziellen Fall als „notlos“ erscheint.

                                        1. Hallo Raketenwilli,

                                          durch Ändern der Voraussetzungen kann man jede Aussage als falsch deklarieren 😉

                                          Man kann $backup_db auch vorher durch den Fuzzi nudeln und das gefuzzte Ergebnis in den heredoc-String einsetzen.

                                          Eine generierte Zeile über mehrere Zeilen Stringverkettung nachzuvollziehen kann nämlich auch Hirnstress auslösen.

                                          Was ich eingestehen muss, ist, dass das $ Zeichen in einem ""-String oder Heredoc-String kritisch sein kann, wenn dahinter Buchstaben folgen. Ist hier nicht der Fall, aber trotzdem ist es natürlich besser, wenn ein Dollarzeichen entweder escaped wird oder man einen ''-String verwendet.

                                          Alternativ könnte ich noch einen nowdoc-String anbieten, gekoppelt mit sprintf weil nun die Variablenersetzung nicht mehr geht:

                                          $backupoption = sprintf(<<<'END'
                                                                   | grep -vP "^USE \`%s\`;$"';
                                                                  END, escape_fuzzi($backup_db));
                                          

                                          Bei genauerer Betrachtung sag ich aber doch eher nö dazu. Dann lieber verketten (was PHP vermutlich beim Erstellen des Bytecodes ohnehin irgendwie optimifiziert).

                                          Rolf

                                          --
                                          sumpsi - posui - obstruxi
                                          1. durch Ändern der Voraussetzungen kann man jede Aussage als falsch deklarieren

                                            Das von mir gebrauchte „Ja, aber“ heißt ausdrücklich nicht „falsch“. Es zeigt nur einen Aber-Fall.

                                            Eine generierte Zeile über mehrere Zeilen Stringverkettung nachzuvollziehen kann nämlich auch Hirnstress auslösen.

                                            Je nach dem. Ich jedenfalls fasse das so besser als lange Einzeiler.

                                            $backupoption = sprintf(<<<'END'
                                            

                                            Ich hatte schon „Übrigens hat noch niemand sprintf() gesagt“ notiert und wieder gelöscht…

                                            sprintf mit inertem heredoc ist ziemliche Gehirnakrobatik. Man muss ziemlich viel abstrahieren - sich Positionen im String, Datentypen und ggf. Formate merken, dann die Argumente wieder zuordnen und dazu noch heredoc…

                                        2. Wobei das escapeshellarg() im konkreten, speziellen Fall als „notlos“ erscheint.

                                          (Fortsetzung)

                                          Aber für SQLITE sähe das anders aus, denn das schreibt in seinem Dialekt die Verwendung von einfachen Quotas (also nicht Backticks) für alle Arten von Namen vor:

                                          $backup_db = 'db0815';
                                          $backupoption = ' | grep -vP '
                                                          . escapeshellarg(
                                                              '^USE \''
                                                              . $backup_db
                                                              . '\';$'
                                                           )   
                                          ;
                                          

                                          Resultat:

                                           | grep -vP '^USE '\''db0815'\'';$'
                                          

                                          Test:

                                          echo -e "USE 'db0815';\nSonstwas" | grep -vP '^USE '\''db0815'\'';$'
                                          

                                          Resultat:

                                          Sonstwas
                                          

                                          🆗

  2. Hallo Jörg,

    ich nutze ein wunderbares Script von Raketenwilli (danke dafür nochmal!), um nachts meine Datenbanken zu sichern.

    Nun wollte ich mal testhalber hiervon eine db wiederherstellen, kriegs aber nicht hin.

    das hilft dir jetzt nicht, aber ... ich versteh's nicht. Wenn ich irgendeine neue Backup-Strategie einführe, dann ist doch mit das erste, was ich versuche, ein Disaster Recovery zur Probe unter kontrollierten Bedingungen.

    Sonst weiß ich doch gar nicht, ob mir dieses Backup im Ernstfall was nützt.

    Einen schönen Tag noch
     Martin

    --
    F: Was ist der Unterschied zwischen einer Henne und einem weißen Blutkörperchen?
    A: Ein weißes Blutkörperchen ist sozusagen ein Eiweißscheibchen, eine Henne dagegen ein Eischeißweibchen.
    1. Hallo Martin,

      das hilft dir jetzt nicht, aber ... ich versteh's nicht. Wenn ich irgendeine neue Backup-Strategie einführe, dann ist doch mit das erste, was ich versuche, ein Disaster Recovery zur Probe unter kontrollierten Bedingungen.

      Naja, das mache ich ja grad. 😉
      Ok, Du wirst einwenden, dass ich das Script ja schon länger nutze.
      Aber zum Einen siehst Du daran, wieviel Vertrauen ich in die Künste von unserem Raketenwilli habe... zum anderen habe ich erst grad vor kurzem meine alte Redundanzsicherung abgeschaltet... und zum dritten sichert auch nochmal mein Provider zusätzlich.
      Du siehst, ich geh nihct ganz unbedarft an die Sache ran.

      Sonst weiß ich doch gar nicht, ob mir dieses Backup im Ernstfall was nützt.

      Naja, noch was.
      Ich hatte den Dump selber auch mal sofort beim ersten Einsatz des Scriptes angesehen, der ist ja Klartext. Und sieht absolut ok aus, soll heißen, manuell einspielen wäre nie ein Problem gewesen.

      Jörg