staging.inyokaproject.org

bash-while-Schleife funktioniert nicht (wie erwartet)

Status: Gelöst | Ubuntu-Version: Ubuntu 18.04 (Bionic Beaver)
Antworten |

Rechnungstore

Anmeldungsdatum:
18. Februar 2011

Beiträge: Zähle...

Hallo Zusammen, ich habe eine Datei (databases.txt), die eine Liste von Datenbanken enthält. Der SQL-Server läuft in einem Docker-Container (auf Basis von Debian 9). Jetzt möchte ich gerne einen SQL-Dump für jede Datenbank in der Liste erstellen. Folgender Befehl erstellt leider nur einen Dump von der ersten Datenbank in der Liste:

1
time while read db; do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT $db > $db.sql; done < databases.txt

wohingegen dieser Befehl (wie gewünscht) von allen in der Liste enthaltenen Datenbanken einen Dump erstellt:

1
time for db in $(cat databases.txt); do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT $db > $db.sql; done

Wo liegt im ersten Befehl mit der while-Schleife der Fehler?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

Der Unterschied könnte daran liegen, dass die erste Variante immer ganze Zeilen nimmt, während die zweite nur Wörter nimmt. Zeig doch mal den Inhalt der DB-Datei.

Rechnungstore

(Themenstarter)

Anmeldungsdatum:
18. Februar 2011

Beiträge: 157

Moin rklm,

aus Datenschutzgründen nicht im Original möglich, aber die Namen der Datenbanken sind nach folgendem Muster aufgebaut:

d_string_nochnstring

Sie enthalten nur Buchstaben und Unterstriche, keine Leerzeichen oder ähnliches. In jeder Zeile steht ein Datenbankname. Wenn ich in der ersten Variante den ganzen Docker-Befehl durch echo ersetze, wird mir (wie erwartet) auch jeder einzelne Datenbankname in einer eigenen Zeile angezeigt.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

Rechnungstore schrieb:

aus Datenschutzgründen nicht im Original möglich, aber die Namen der Datenbanken sind nach folgendem Muster aufgebaut:

Bitte schick dann mal eine Datei, die so aussieht wie das Original - nur halt mit den echten Namen ersetzt. Es geht wirklich darum, das Format der einzelnen Einträge als auch das der Datei zu sehen.

Sie enthalten nur Buchstaben und Unterstriche, keine Leerzeichen oder ähnliches. In jeder Zeile steht ein Datenbankname. Wenn ich in der ersten Variante den ganzen Docker-Befehl durch echo ersetze, wird mir (wie erwartet) auch jeder einzelne Datenbankname in einer eigenen Zeile angezeigt.

Lass mal folgendes Kommando auf die Datei los. Damit kannst Du sehen, ob es da Leerzeichen am Zeilenende gibt:

1
sed 's#$#|#' datei

Rechnungstore

(Themenstarter)

Anmeldungsdatum:
18. Februar 2011

Beiträge: 157

Es befand sich tatsächlich ein Leerzeichen in Zeile 9. Das Entfernen hat aber nichts geändert.

Die Datei:

database.txt

Das Format der Datei

1
2
root@server:/backup/dbdump_temp# file databases.txt 
databases.txt: ASCII text

Doc_Symbiosis

Avatar von Doc_Symbiosis

Anmeldungsdatum:
11. Oktober 2006

Beiträge: 4212

Hm, also eigentlich sieht der erste Befehl soweit richtig aus.

Was passiert denn, wenn Du einfach ein "echo $db" dort einsetzt statt dem docker-Befehl?

Wurde die Liste unter Linux erstellt oder sind da vielleicht Windows-Zeilenumbrüche drin?

Rechnungstore

(Themenstarter)

Anmeldungsdatum:
18. Februar 2011

Beiträge: 157

Hallo Doc_Symbiosis,

das mit echo habe ich probiert. Funktioniert wie erwartet. Die Liste wurde auf demselben Ubuntu 18.04 mit vim erstellt.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

Für die beiden Befehle mal in einer Shell aus, wo Du vorher set -x gesagt hast. Ansonsten fällt mir nur ein, die Variablen zu quoten:

1
2
time while read db; do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT "$db" > "$db.sql"; done < databases.txt
time for db in $(cat databases.txt); do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT "$db" > "$db.sql"; done

Vielleicht macht das ja den Fehler sichtbar.

Rechnungstore

(Themenstarter)

Anmeldungsdatum:
18. Februar 2011

Beiträge: 157

1
2
3
4
5
6
7
8
9
root@server:/backup/dbdump_temp# time while read db; do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT "$db" > "$db.sql"; done < databases.txt
+ read db
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_string_jh
Warning: Using a password on the command line interface can be insecure.
+ read db

real	0m5.168s
user	0m0.213s
sys	0m0.502s
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

root@server:/backup/dbdump_temp# time for db in $(cat databases.txt); do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT "$db" > "$db.sql"; done
++ cat databases.txt
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.
+ for db in $(cat databases.txt)
+ docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT c_dbname
Warning: Using a password on the command line interface can be insecure.

real	0m32.221s
user	0m1.252s
sys	0m2.200s

In dem zweiten Befehl steht in der Ausgabe natürlich jedes mal der echte Name der jeweiligen Datenbank aus der Liste und nicht "c_dbname".

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 7816

Probiere es einmal ohne time.

Rechnungstore

(Themenstarter)

Anmeldungsdatum:
18. Februar 2011

Beiträge: 157

Probiere es einmal ohne time.

Habe ich schon ganz am Anfang getestet. Kein Unterschied. Naja, egal letztlich bin ich ja mit der For-Schleife ans Ziel gekommen. Es ärgert mich immer nur ein wenig, wenn ich so grundlegende Dinge nicht verstehe.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

Rechnungstore schrieb:

Naja, egal letztlich bin ich ja mit der For-Schleife ans Ziel gekommen. Es ärgert mich immer nur ein wenig, wenn ich so grundlegende Dinge nicht verstehe.

Würde mir genau so gehen. Man sieht ja, dass der zweite read scheitert. Probier doch mal

1
while read db; s=$?; echo "Status $s"; ( exit $s ); do echo "read: <$db>"; done < databases.txt

Vielleicht ist das ja erhellend. Siehe help read wg. Rückgabewerten.

Ruth-Wies

Anmeldungsdatum:
12. April 2023

Beiträge: Zähle...

OT

Rechnungstore schrieb:

… in einem Docker-Container (auf Basis von Debian 9). …

wiki.debian.org/LTS schrieb:

Debian LTS support for Debian 9 "Stretch" ended on June 30, 2022

Gibt es da nichts aktuelleres? Gleiches gilt für 18.04; das hat nur noch 49 Tage (bis Ende Mai).

shiro

Anmeldungsdatum:
20. Juli 2020

Beiträge: 611

Hmm... Ich hatte ein vergleichbares Problem mit HandBrakeCLI. Nach Abarbeiten der ersten Datei in der while loop wurden die loop beendet.

Eine "while" Schleife ist leider nicht so stabil, wenn das in der loop aufgerufene Programm von stdin liest. Ich habe das Problem so umschifft, indem ich vor dem Aufruf des Programms ein 'echo "" | ' als pipe gesetzt habe.

Dies wäre dann im Beispiel wie folgt zu implementieren:

time while read db; do echo "" | docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT $db > $db.sql; done < databases.txt

Ein Versuch wäre es aus meiner Sicht wert.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

shiro schrieb:

Eine "while" Schleife ist leider nicht so stabil, wenn das in der loop aufgerufene Programm von stdin liest.

Ah! Guter Punkt! Das wird es sein.

Ich habe das Problem so umschifft, indem ich vor dem Aufruf des Programms ein 'echo "" | ' als pipe gesetzt habe.

Es gibt noch zwei andere Varianten:

  1. Stdin für diesen Prozess schließen

  2. read von einem anderen Dateideskriptor lesen lassen.

Kann es gerade nicht testen, aber so ungefähr:

1
2
3
4
5
6
# 1
time while read db; do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT $db > $db.sql <&-; done < databases.txt

# 2
exec 9<databases.txt
time while read db <&9; do docker exec -u 0 -i 5ae0a08b41d3 mysqldump -uUSER -pPASSWORT $db > $db.sql; done

Ggf. Variante 2 in eine Subshell packen, damit FD 9 automatisch wieder geschlossen wird. Ansonsten geht das auch mit exec 9<&-.

Antworten |