Wichtiges Problem mit Expect Script. ( Ausgabe in Datei )

Message
Author
jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

Wichtiges Problem mit Expect Script. ( Ausgabe in Datei )

#1 Post by jochen123 »

Hallo,

ich hab hier ein sehr wichtiges Problem mit einem Expect Script.
Das Script meldet sich auf einem sftp Server an und läd dort 1 Ordner hoch und 3 Ordner runter. ( Also den Inhalt der einzelnen Ordner )

Nun müssen aber alle kopierten Dateien die hochgeladen / runtergeladen wurden gelöscht werden. Da das Script alle paar Minuten laufen muss und ich somit nicht einfach ein "rm" auf Ordner/* machen kann, da ich sonst neue Dateien lösche die beim Ausführen des Scriptes in die Ordner gelegt wurden.

Die Chance das das passiert ist aufgrund der paar Minuten die zwischen Ausführung des Scriptes liegen sehr hoch.

Da man mit SFTP ja nicht verschieben kann wollte ich es über eine Variable lösen.
Ich wil also nun alle kopierten Dateien in eine Datei/Variable schreiben und dann sagen
rm $Variable damit nur die Dateien gelöscht werden die auch kopiert wurden.

Ein einfaches > ausgabe_datei geht ja nicht in expect so wie ich das sehe :(

Hier mal das Script:


#######################################################################
#!/usr/bin/expect -f

# Variablen setzen
set date [exec date +%m-%d-%y]
log_file /var/log/$date.kt_sftp.log

# Erlaeuterung zur Bedienung
if {$argc!=1} {
send_user "
usage: $argv0 Password \n
"
exit
}

# Zusaetzliche Optionen
set timeout -1
match_max 100000
#log_user 0

# Password einlesen
set pw [lindex $argv 0]

# sftp Verbindung herstellen
spawn sftp user@IP/Host

# Bei erster Verbindung "yes" zurueckgeben und dann das Passwort, ansonsten nur das Passwort
expect {
"*you sure you want to*" {
send "yes\r"
exp_continue
}
"*password:*"
}
send "$pw\r"

# Datein kopieren
expect "sftp>"
send "put Lokaler_Ordner1/* Externer_Ordner1/ \r"

expect "sftp>"
send "get Externer_Ordner2/* Lokaler_Ordner2/ \r"

expect "sftp>"
send "get Externer_Ordner3/* Lokaler_Ordner3/ \r"

expect "sftp>"
send "get Externer_Ordner4/* Lokaler_Ordner4/ \r"

# Session beenden
expect "sftp>"
send "exit \r"

expect eof
#######################################################################


Wie kann ich nun die durch put und get kopierten Datein nach dem kopieren wieder löschen. Und NUR diese Datein löschen.
Also in eine Variable/Datei ausgeben und dann löschen der Datein die in der Variable/Datei stehen.

So habe ich mir das gedacht bekomme es aber nicht hin.


Über eine schnelle Hilfe wäre ihr sehr verbunden :)

Gruß
Jochen

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#2 Post by Janka »

Expect ist tcl.

Das bedeutet, dass du dir einfach mal die Tcl-Doku anschauen musst, um etwas über die Syntax dieser Sprache zu erfahren.

String in Datei schreiben:

Code: Select all

set FD [ open Dateiname w+ ]
puts $FD $FILENAMES
close $FD
String aus Datei lesen:

Code: Select all

set FD [ open  Dateiname r ]
set FILENAMES [ read $FD ]
close $FD
Dateien löschen kann man mit "rm" (dann wird das Shellkommando aufgerufen) oder intern mit "file delete"

Nützlich ist in diesem Fall sicher auch noch "foreach":

Code: Select all

foreach FILE $FILENAMES {
  file delete $FILE
}
Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#3 Post by jochen123 »

Erst mal danke für die Antwort.
Das scheint ja genau das zu sein was ich brauche.

Aber wie verschachtel ich das mit meinem sftp anweisungen. ( get / put )

expect "sftp>"
MUSS JA DANN HIER REIN IRGENDWO?
send "put Lokaler_Ordner1/* Externer_Ordner1/ \r"

Und $FILENAMES hab ich doch nicht als Variable? Ich müsste dann doch erst die kopierten Datein in $FILENAMES schreiben oder nicht. Blos wie.

Kannst du oder wer anders mir für einen get Befehl das drumrum schreiben?

Das Script muss ich bald fertig haben aber ich blick durch expect in der tiefe wie gerade nicht durch und hab durch andere Projecte keine Zeit mich so tief einzulesen.
( Was ich aber definitiv machen werden weil es verdammt intressant ist was man damit alles machen kann)

Wäre super wenn mir da einer nen Beispiel für mein Script schreiben könnte.

Gruß Jochen und danke im Vorraus

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#4 Post by Janka »

An das Ergebnis einer Operation (Beispielsweise "ls" im Kontext von sftp) kommst du heran, indem du expect ein Skript mitgibst, in dem du $expect_out(buffer) in eine andere Variable kopierst.

Fertige Skripte: Nee, von mir nicht. Es sei denn, du gibst mir fertiges Geld...

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#5 Post by jochen123 »

Hallo,

habe jetzt mal ein bisschen rumprobiert.
Allerdings schreibt er mir nur sftp> in meine test Datei.

Hier der Auschnitt:

set FD [open "/tmp/test" "w"]

expect "sftp>"
send "put /mnt/Eingang/* Eingang/ \n"

set variable $expect_out(buffer)
puts $FD $variable
close $FD

Er schreibt mit aber nicht die kopierten Daten in den buffer.
Wo hab ich da den jtzt den denk Fehler?

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#6 Post by Janka »

Das finden des richtigen Suchfilters für expect kann eine Qual sein. Hier ein Beispiel mit bash als Shell und ls als Kommando innerhalb dieser shell:

Code: Select all

#!/usr/bin/expect -f
set timeout -1 
match_max 100000 

spawn bash --noprofile --norc
 
send "ls -1 --color=never\n"
expect "bash-3.1\\$*bash-3.1\\$"

set fd [ open "result" w ]
puts $fd "-----------------\n$expect_out(buffer)\n-----------------"
close $fd

send "exit\n" 
expect eof
Wobei das Beispiel auch noch nicht perfekt ist, da das Prompt und das Kommando im Ergebnis mit auftauchen.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#7 Post by jochen123 »

Das Problem habe ich in den Griff bekommen.

Ich löse es nun so indem ich erst einen "ls" Befehlt aus dem SFTP-Server ausführe (ls -1) und das Ergebniss dieses Befehls in eine Datei ausgebe.

Kopiere dann nur die gelisteteten Dateien und lösche nur die gelisteten Datein.

Hier mit klappt das super.

set output [open "/tmp/ausgang_list" "w"]
expect -re "(> )" { send "ls -1 \n" }
expect -re "\r\n(.*)\r(.*)(\\\$ |> )"
set outcome $expect_out(1,string)
send "\r"
puts $output $outcome
close $output

Danach würde ich halt mit deinem "for each file" Beispiel weiter machen. Denke das klappt.


Habe nur ein Problem :(
Wenn durch meinen "ls -1" Befehl keine Ergebnisse erziehlt werden. Sprich wenn keine Dateien in dem Ordner vorhanden sind dann macht mein Script nicht weiter und bleibt auf dem sftp Server angemeldet.
Kannst du mir da helfen? So dass wenn ls -1 keine treffer erhält das er trotztdem weiter macht?

Habe das mal so probiert aber das haut net hin.


#set output [open "/tmp/ausgang_list" "w"]
#expect -re "(> )" { send "ls -1 \n" }
# expect -re {
# "\r\n(.*)\r(.*)(\\\$ |> )" {
# set outcome $expect_out(1,string)
# send "\r"
# puts $output $outcome
# close $output
# exp_continue
#}
#"sftp>"
#}
#send "\r"

Er bleibt aber immer hängen wenn er nichts findet.

Gruß und danke für die Hilfe

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#8 Post by Janka »

Mach statt "ls -1" einfach "ls -a1", und filtere "." und ".." danach weg.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#9 Post by jochen123 »

Um etwas aus zu filtern kenn ich mich nicht gut genug aus. Weis net wie das geht.

Das muss doch ganz einfach mit exp_continue gehen blos mach ich irgendwas falsch :(

Wie ist eigentlich der Befehl in expect zum warten.
So das ich warte bis er die Datein gelöscht hat bevor er weiter macht?

Ist da "after 5000" für 5 sek richtig? oder gibts da was besseres?

Das mit after klappt zwar und Datein werden auch gelöscht aber irgendwie ist da was komisch weil er dann bei den nächsten Ordner nur "ls -1" in meine anderen beiden output Dateien schreibt oO

Nehme ich den den "after" Befehl raus gehen die beiden Outputs (variablen die ich in ne Datei schreibe) ???

Ich bastel mal noch nen bisschen Tag ist ja noch jung ;)

Vielleicht hast du ja noch nen hilfreichen Tipp. Wenn alles fertig ist (muss es bis morgen) poste ich das ganze Script dann mal hier

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#10 Post by jochen123 »

Wichtigste Frage.

Wie kann ich ihm sagen das er warten soll bis ein Befehl ausgeführt wurde?

Wenn ich alle Dateien in $files kopiere muss er warten bis diese kopiert sind bevor er die $files löscht. Nicht das ich anfange zu lösche bevor er mit kopieern fertig ist.

Macht er das eh oder gibts da nen Befehl. Hoffe mein Buch kommt mal bald an :)

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#11 Post by jochen123 »

So und nun zum Abschluss noch mal der Status und so beschrieben das man auch was versteht :)

Das Script läuft beim kopieren von nur einem Ordner SUPER bloß wenn ich alle Ordner kopieren will ( 3 downloaden und einen uploaden ) klappt es überhaupt nicht da er die Befehle zu schnell hinternander ausführt.
Für den ersten Ordner kopiert er sauber die Datein und löscht diese dann auch aber bei den nächsten schreibt er müll in die Variablen und probiert deswegen komische Sachen zu löschen.

Hier mal das ganze Script wo nur noch fehlt das er wartet bis die Dateien kopiert und gelöscht wurden bevor er mit dem nächsten Ordner weiter macht.

Hoffe dafür hast du oder wer anders ne Lösung. Habe es schon mit "sleep5" und "after 5000" hinter jedem kopier und lösch Befehl probiert, dann wartet er zwar kommt wenn er weiter macht aber wieder nicht klar und macht müll.
Vielleicht ist ja "wait" der richtige Befehl aber glaub der geht nur für den ganzen sftp prozess oder?



#!/usr/bin/expect -f

# Variablen setzen
set date [exec date +%m-%d-%y]
log_file /var/log/$date.kt_sftp.log

# Erlaeuterung zur Bedienung
if {$argc!=1} {
send_user "
usage: $argv0 Password \n
"
exit
}

# Zusaetzliche Optionen
set timeout -1
match_max 100000
#log_user 0

# Password einlesen
set pw [lindex $argv 0]

# Lokale Liste vom Eingang holen
spawn /bin/bash
sleep 0.5
send "cd /tmp/Eingang/ \n"
set eingang_int_output [open "/tmp/eingang_int_list" "w"]
expect -re "(# )" { send "ls -1 \n" }
expect -re "\r\n(.*)\r(.*)(\\\$ |# )"
set eingang_int_outcome $expect_out(1,string)
send "\r"
puts $eingang_int_output $eingang_int_outcome
close $eingang_int_output

set eingang_int_input [ open "/tmp/eingang_int_list" "r" ]
set eingang_int_files [ read $eingang_int_input ]
close $eingang_int_input

# sftp Verbindung herstellen
spawn sftp user@host

# Einloggen
expect {
"*you sure you want to*" {
send "yes\r"
exp_continue
}
"*password:*"
}
send "$pw\r"

# Externen Ordner Eingang nach intern kopieren
expect "sftp>"
send "cd Eingang \n"

set eingang_output [open "/tmp/eingang_list" "w"]
expect -re "(> )" { send "ls -1 \n" }
expect -re "\r\n(.*)\r(.*)(\\\$ |> )"
set eingang_outcome $expect_out(1,string)
send "\r"
puts $eingang_output $eingang_outcome
close $eingang_output

set eingang_input [ open "/tmp/eingang_list" "r" ]
set eingang_files [ read $eingang_input ]
close $eingang_input

foreach eingang_file $eingang_files {
send "get $eingang_file /tmp/Ausgang/ \n"
}

foreach eingang_file $eingang_files {
send "rm $eingang_file \n"
}

expect "sftp>"
send "cd ../Quittungen \n"

set quittungen_output [open "/tmp/quittungen_list" "w"]
expect -re "(> )" { send "ls -1 \n" }
expect -re "\r\n(.*)\r(.*)(\\\$ |> )"
set quittungen_outcome $expect_out(1,string)
send "\r"
puts $quittungen_output $quittungen_outcome
close $quittungen_output

set quittungen_input [ open "/tmp/quittungen_list" "r" ]
set quittungen_files [ read $quittungen_input ]
close $quittungen_input

foreach quittungen_file $quittungen_files {
send "get $quittungen_file /tmp/quittungen/ \n"
}

foreach quittungen_file $quittungen_files {
send "rm $quittungen_file \n"
}

expect "sftp>"
send "cd ../P-Ausgang \n"

set p_output [open "/tmp/p_ausgang_list" "w"]
expect -re "(> )" { send "ls -1 \n" }
expect -re "\r\n(.*)\r(.*)(\\\$ |> )"
set p_outcome $expect_out(1,string)
send "\r"
puts $p_output $p_outcome
close $p_output

set p_input [ open "/tmp/p_ausgang_list" "r" ]
set p_files [ read $p_input ]
close $p_input

foreach p_file $p_files {
send "get $p_file /tmp/P-Ausgang/ \n"
}

foreach p_file $p_files {
send "rm $p_file \n"
}

expect "sftp>"
send "cd ../Ausgang \n"

foreach eingang_int_file $eingang_int_files {
send "put $eingang_int_file \n"
}

# Session beenden
expect "sftp>"
send "exit \r"

# Lokalen Eingang leeren
spawn /bin/bash
send "cd /tmp/Eingang \n"
send "rm $eingang_int_file \n"

expect eof



Hoffe du oder wer anders kann mir mit dem Problem helfen das er wartet bis Daten kopiert wurden bevor er weiter geht im Script.

Verbesserungen nehme ich gerne an :)

Icke geh nu schlafen. Genug für heute.

Danke für die hilfe.

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#12 Post by Janka »

Flitern: Einfach eine Schleife über den an Zeilenenden getrennten String machen. Dabei alle Zeilen mit . oder .. überspringen.

Code: Select all

set FILES {}
foreach FILE [ split $expect_out(1,string) \n ] {
  if { ($FILE eq ".") || ($FILE eq "..") } continue

  lappend FILES $FILE
}
Danach ist in FILES eine Liste von Dateinamen, die man mit foreach weiterverarbeiten kann.

Warten: Überleg mal, du startest doch mit spawn einen zweiten Prozess. expect und der andere Prozess sind unsychroniert, nur indem man die Ausgaben des anderen Prozesses mit "expect" prüft, kann man sie synchronisieren.

Der andere Prozess führt die an ihn gegebenen Befehle so aus wie getippt. Wenn du nacheinander put, rm übergibst, wird das auch in der Reihenfolge ausgeführt.

Dein Problem liegt vermutlich darin, dass du nicht auf den Prompt wartest, bevor du das nächste Verzeichnis bearbeitest. Dann "tippt" expect , bevor ein Prompt kommt, und diese Eingabe das wir von sftp ignoriert.

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#13 Post by jochen123 »

Danke für die hilfe. Das mit den Filtern gucke ich mir morgen mal an.

Mit dem warten ist mir noch nicht ganz klar. Ich mache doch eigentlich ein expect "sftp>". oder schreibt der das auch beim kopieren dahin? Gucke ich morgen mal nach.

Ein Kollege meinte das das mit dem Warten nicht geht weil es halt ein gespawnter Prozess ist und ich für jede Operation (ls, put , del) einen eigenen sftp Prozess spawnen muss.
Das würde glaube ich klappen mit dem "wait" Befehl von expect. Der ist ja für prozesse da so wie ich das gelesen habe. Gucke ich mir morgen noch mal an.

Aber muss doch auch einfachermöglich sein oder nicht?

User avatar
Janka
Posts: 3585
Joined: 11. Feb 2006 19:10

#14 Post by Janka »

jochen123 wrote:Danke für die hilfe. Das mit den Filtern gucke ich mir morgen mal an.

Mit dem warten ist mir noch nicht ganz klar. Ich mache doch eigentlich ein expect "sftp>". oder schreibt der das auch beim kopieren dahin? Gucke ich morgen mal nach.
Wie schon gesagt, das Finden der richtigen Pattern ist eine Qual. Du hast außerdem vor mehreren "send" in den foreach-Schleifen kein Warten auf das Prompt (expect "sftp>").
Ein Kollege meinte das das mit dem Warten nicht geht weil es halt ein gespawnter Prozess ist
Nein. Was dein Kollege vermutlich meinte, ist , dass *lokales* Warten, also "after" nichts bringt. Aber anderer Stelle "wartet" expect ja auch auf den gespawnten Prozess: Wobei "warten" nicht der eigentliche Vorgang ist: expect kopiert solange die Standardausgabe des gespawnten Prozesses in expect_out(buffer), bis das Pattern gefunden wird.

Der gespawnte Prozess wartet automatisch, sobald sein Augabepuffer voll ist, expect also nichts abholt. Deshalb muss man als letzte Operation also nochmal nach einem eof suchen.

Dein Skript ist auch an einigen Stellen sehr umständlich. Die ganzen Dateien /tmp/... werden beispielsweise nicht benötigt, die Dateinamen stehen ja bereits in den Variablen drin. Das kann man also beim nächsten foreach einsetzen. Außerdem muss man daraus mit split noch saubere Listen machen.

Zudem kann man in {}-Blöcke mehrere Anweisungen (send "get..." und send "rm...") einfügen.

Ich hab also mal aufgeräumt:

Code: Select all

#!/usr/bin/expect -f 
 
# Variablen setzen 
set date [exec date +%m-%d-%y] 
log_file /var/log/$date.kt_sftp.log 
 
# Erlaeuterung zur Bedienung 
if {$argc!=1} { 
 send_user " 
usage: $argv0 Password \n 
" 
 exit 
} 
 
# Zusaetzliche Optionen 
set timeout -1 
match_max 100000 
#log_user 0 
 
# Password einlesen 
set pw [lindex $argv 0] 
 
# Lokale Liste vom Eingang holen 
set eingang_int_files [ glob /tmp/Eingang/* ]
 
# sftp Verbindung herstellen 
spawn sftp user@host 
 
# Einloggen 
expect { 
 "*you sure you want to*" { 
 send "yes\r" 
 exp_continue 
 } 
 "*password:*" 
} 
send "$pw\r" 
 
# Externen Ordner Eingang nach intern kopieren 
expect "sftp>" 
send "cd Eingang\n" 
 
expect -re "(> )" { send "ls -a1\n" } 
expect -re "\r\n(.*)\r(.*)(\\\$ |> )" 
set eingang_extern_files [ split $expect_out(1,string) \n ]
send "\r" 
 
foreach eingang_file $eingang_extern_files { 
  if { ($eingang_file eq ".") || ($eingang_file eq "..") } continue
  expect "sftp>" 
  send "get $eingang_file /tmp/Ausgang/\n" 
  expect "sftp>" 
  send "rm $eingang_file\n" 
} 
 
expect "sftp>" 
send "cd ../Quittungen\n" 
 
expect -re "(> )" { send "ls -a1\n" } 
expect -re "\r\n(.*)\r(.*)(\\\$ |> )" 
set quittungen_files [ split $expect_out(1,string) \n ]
send "\r" 
 
foreach quittungen_file $quittungen_files { 
  if { ($quittungen_file eq ".") || ($quittungen_file eq "..") } continue
  expect "sftp>" 
  send "get $quittungen_file /tmp/quittungen/\n" 
  expect "sftp>" 
  send "rm $quittungen_file\n" 
} 
 
expect "sftp>" 
send "cd ../P-Ausgang\n" 
 
expect -re "(> )" { send "ls -a1\n" } 
expect -re "\r\n(.*)\r(.*)(\\\$ |> )" 
set p_files [ split $expect_out(1,string) \n ]
send "\r" 

foreach p_file $p_files { 
  if { ($p_file eq ".") || ($p_file eq "..") } continue
  expect "sftp>" 
  send "get $p_file /tmp/P-Ausgang/ \n" 
  expect "sftp>" 
  send "rm $p_file\n" 
} 
 
expect "sftp>" 
send "cd ../Ausgang\n" 
 
foreach eingang_int_file $eingang_int_files { 
  expect "sftp>" 
  send "put $eingang_int_file\n" 
  file delete $eingang_int_file
} 
 
# Session beenden 
expect "sftp>" 
send "exit \r" 
expect eof 

Janka
Ich vertonne Spam immer in /dev/dsp statt /dev/null.
Ich mag die Schreie.

jochen123
Posts: 23
Joined: 21. Aug 2006 11:45

#15 Post by jochen123 »

Oha wieder mal was gelernt :)

Danke fürs aufräumen. Werde mich nun mal dran setzen und weiter testen.

Post Reply