I have a expect script that spawn a process. The process ends quickly and sometimes it takes few seconds. It is a SQL query.
I have tried different things at the end of the script but still get the error below sometimes
expect: spawn id exp7 not open
Things tried:
1. interact
2. expect eof
3. exp_continue
4. expect eof
catch wait result
What is the fool proof way to let the process and then exit expect script?
#!/usr/local/bin/expect
###exp_internal -f debug_info.log 0;
set username [lindex $argv 0]
set firstname [lindex $argv 1]
set lastname [lindex $argv 2]
set mypassword [lindex $argv 3]
set userpassword [lindex $argv 4]
set LOG_FILE [open /home/applusr/e291505/logs/ADD.log a]
set today [ exec /bin/date +%Y-%m-%d-%T]
set ::env(sec) /home/root/admin
puts $LOG_FILE "\n------------------ADD_SCRIPT - $today----$username--$firstname--$lastname--$env(sec)-"
close $LOG_FILE
set timeout 10
log_user 0
log_file -a /home/applusr/e291505/logs/ADD.log
spawn $env(sec)/add.mims.user $username "$firstname $lastname"
set addID $spawn_id
expect "e291505's Password:*" { send "$mypassword\n" }
expect "$username's New password:*" { send "$userpassword\n" }
expect "Enter the new password again:*" { send "$userpassword\n" }
expect "Password:" { sleep 1; send "$mypassword\n" }
##interact
This is how you handle things to conditionally expect:
spawn $env(sec)/add.mims.user $username "$firstname $lastname"
set addID $spawn_id
expect "e291505's Password:*"
send -- "$mypassword\n"
expect "$username's New password:*"
send -- "$userpassword\n"
expect "Enter the new password again:*"
send -- "$userpassword\n"
expect {
"Password:" { send -- "$mypassword\n"; exp_continue }
eof
}
The last expect command will find that password prompt or the end of the process, whichever happens first. If the password prompt is seen, the exp_continue command "loops" within that expect command so that you keep waiting to see eof.
Note that I tweaked the send commands: you are now protected from any of the passwords starting with a hyphen.
Related
I try to automate some tasks with an exepect file, but when I try to send cat and display my file nothing happens, the file exists, I can display it when I type the command manually.
I have the impression that it is working in the background, but this is not the result I expect.
#!/usr/bin/expect
set ip [ lindex $argv 0 ]
set port [ lindex $argv 1 ]
set user [ lindex $argv 2 ]
set password [ exec cat "../../flag04/flag" ]
spawn ssh "$user\#$ip" "-p $port"
expect "password:" { send "$password\r" }
send "echo 'bin/getflag >> /tmp/flag05' >> /opt/openarenaserver/script.sh\r"
expect ":~$" { send "cat /tmp/flag05\r" }
interact
I just find the anwser, it missed a space after :$ after the dollar sign
For the following script, the if block in expect language is not working:
#!/usr/bin/expect -f
set timeout -10
set prompt {$}
spawn ssh 1.1.1.1
expect -re "$prompt"
send -- "echo 12\r"
expect {
"12" {send "echo hunky-dory\r"}
"bg" {send "echo 1\r"}
"ih" {send "echo 2\r"}
}
send -- "exit\r"
interact
The code will echo 12. However, it does not obey the if block and so does not echo hunky-dory after the echo 12, as has been clearly argued by my script.
Out put with #!/usr/bin/expect -d:
expect version 5.45
argv[0] = /usr/bin/expect argv[1] = -d argv[2] = ./testexpect.exp
set argc 0
set argv0 "./testexpect.exp"
set argv ""
executing commands from command file ./testexpect.exp
spawn ssh <hidden for security reasons>
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {3920}
send: sending "echo 12\r" to { exp4 }
expect: timed out
send: sending "exit\r" to { exp4 }
tty_raw_noecho: was raw = 0 echo = 1
spawn id exp4 sent <echo 12\r\nexit\r\n>
echo 12
exit
My expect block is not even being recognized.
Your problem is this:
set timeout -10
expect treats that negative value like zero. There is literally no time assigned for the expect block to act, and you go directly to send exit
Use either -1 or some value greater than zero for the timeout value.
When running a section of my tcl script I would like to warn certain users with open terminals of what is happening. At the same time letting them know which terminal the message is being sent from. So far I have:
set who [exec who]
set user [lindex $who 0]
set who [split $who "\n"]
for { set i 0 } { $i < [llength $who] } { incr i } {
set current [lindex $who $i]
exec write $user [lindex $current 1]
# I would now like to send a message to this user
}
Except when I hit the "write" section the code gets stuck. As it can't exit. How do I pass tcl for shell variables to be written to desired user's terminal? Then exit the write section?
It does not look like i can use the "wall" command in linux.
You also need to supply an actual message to send. Assuming you are sending the same message to each, just put the message in a variable at the top of your script and use a heredoc with exec to supply it as write's standard input.
set who [exec who]
set user [lindex $who 0]
set who [split $who "\n"]
set message "The quick brown fox is jumping over the lazy dog.\n"
for { set i 0 } { $i < [llength $who] } { incr i } {
set current [lindex $who $i]
exec write $user [lindex $current 1] << $message
}
I have an expect script that looks like:
#!/usr/bin/expect
set path_start [lindex $argv 0]
set host [lindex $argv 1]
spawn ssh root#$host telnet jpaxdp
expect {\-> }
set fh [open ${path_start}${host} r]
while {[gets $fh line] != -1} {
send "$line\r"
expect {\-> }
}
close $fh
send "exit\r"
expect eof
and I call it like ./script.sh cmds_ cc1, now my hosts are numbered 1 - 8 and I tried to call the script like ./script cmds_ cc[1-8] but that didn't work as the script interpreted host[1-8] as argument and showed me:
spawn ssh root#cc[1-8] telnet jpaxdp
ssh: Could not resolve hostname cc[1-8]: Name or service not known
couldn't open "cmds_cc[1-8]": no such file or directory
while executing
"open ${path_start}${host} r"
invoked from within
"set fh [open ${path_start}${host} r]"
(file "./script.sh" line 7)
How can I make this work?
cc[1-8] is a filename wildcard, it looks for files that match that pattern. If there aren't any, the wildcard itself is kept in the argument list. To get a range of numbers, use cc{1..8}. And to run the command repeatedly, you need a for loop.
for host in cc{1..8}
do
./script.sh cmds_ "$host"
done
I was able to transfer files with scp and expect, now I tried to upload several files at once:
#!/usr/bin/expect -f
# Escapes spaces in a text
proc esc text {
return [regsub -all {\ } $text {\\&}]
}
# Uploads several files to a specified server
proc my_scp_multi {ACCOUNT SERVER PW files newfolder} {
set timeout 30
send_user -- "\n"
spawn scp $files $ACCOUNT#$SERVER:[esc $newfolder]
match_max 100000
# Look for password prompt
expect {
-re ".*Connection closed.*" {
sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nThis is most likely induced by too many wrong password-attempts and will last quite a time!"
}
-re ".*Permission denied.*" {
sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nYou entered most likely a wrong password!"
}
-re ".*Are.*.*yes.*no.*" {
send "yes\n"
exp_continue
#look for the password prompt
}
-re ".*sword.*" {
# Send password aka $PW
send -- "$PW\r"
# send blank line (\r) to make sure we get back to gui
send -- "\r\n"
exp_continue
}
send_user -- "Upload successful!\n"
}
set timeout -1
}
When I want to upload several files, the sh command is:
scp $a $b $c user#server:$folder, so I called my_scp_multi "ACCOUNT" "SERVER" "PW" "~/testfileA ~/testfileB ~/testfileC" "~/test/". Which also produces this output:
spawn scp ~/testfileA ~/testfileB ~/testfileC user#server:~/test/
user#server's password:
~/testfileA ~/testfileB ~/testfileC: No such file or directory
It seems to see "~/testfileA ~/testfileB ~/testfileC" as one file. But when I copy-paste scp ~/testfileA ~/testfileB ~/testfileC user#server:~/test/ to the console it works fine!
What am I doing wrong? I've tried "\"~/testfileA\" \"~/testfileB\" \"~/testfileC\"" and such things, but nothing did work at all.
Any ideas or suggestions?
EDITS
P.S.: I'm transferring rather small files. Building up a connection is the biggest part of the transfer. This is the reason I want it to be done in ONE scp.
P.P.S.:
I played around a little and came up with:
my_scp_multi3 "user" "server" "pw" "~/a\ b/testfileA, ~/a\\ b/testfileB, ~/a\\\ b/testfileC" "~/test"
with your first solution but {*}[split $files ","] and
my_scp_multi2 "user" "server" "pw" "~/a b/testfileA" "~/a\ b/testfileB" "~/a\\ b/testfileC" "~/test"
with your second solution. This prints:
~/a b/testfileA: No such file or directory
~/a\ b/testfileB: No such file or directory
~/a\ b/testfileC: No such file or directory
and
~/a b/testfileA: No such file or directory
~/a b/testfileB: No such file or directory
~/a\ b/testfileC: No such file or directory
(BTW: I of course moved the files :) )
Thanks to all the answers, here my Solution:
using \n \0 (nullbyte) as separator, because it is the only symbol except / and \ which may not be used in filenames.
#!/usr/bin/expect -f
# Escapes spaces in a text
proc esc text {
return [regsub -all {\ } $text {\\&}]
}
# Returns the absolute Filepath
proc makeAbsolute {pathname} {
file join [pwd] $pathname
}
proc addUploadFile {files f} {
if {$files != ""} {
set files "$files\0"
}
return "$files[makeAbsolute $f]"
}
#Counts all files from an upload-list
proc countUploadFiles {s} {
set rc [llength [split $s "\0"]]
incr rc -1
return $rc
}
# Uploads several files from a list (created by addUploadFile) to a specified server
proc my_scp_multi {ACCOUNT SERVER PW files newfolder} {
foreground blue
set nFiles [countUploadFiles $files]
set timeout [expr $nFiles * 60]
send_user -- "\n"
spawn scp -r {*}[split $files "\0"] $ACCOUNT#$SERVER:[esc $newfolder]
match_max 100000
# Look for password prompt
expect {
-re ".*Connection closed.*" {
sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nThis is most likely induced by too many wrong password-attempts and will last quite a time!"
}
-re ".*Permission denied.*" {
sendError "\n\n\nUpload failed!\nPlease check the errors above and start over again.\nYou entered most likely a wrong password!"
}
-re ".*Are.*.*yes.*no.*" {
send "yes\n"
exp_continue
#look for the password prompt
}
-re ".*sword.*" {
# Send password aka $PW
send -- "$PW\r"
# send blank line (\r) to make sure we get back to gui
send -- "\r\n"
exp_continue
}
send_user -- "Upload successful!\n"
}
set timeout -1
}
set fls [addUploadFile "" "a b/testfileA"]
set fls [addUploadFile $fls "a b/testfileB"]
set fls [addUploadFile $fls "a b/testfileC"]
my_scp_multi "user" "server" "pw" $fls "~/test"
You don't want to send the filenames as a single string. Either do this:
spawn scp {*}[split $files] $ACCOUNT#$SERVER:[esc $newfolder]
And continue to quote the filenames:
my_scp_multi "ACCOUNT" "SERVER" "PW" "~/testfileA ~/testfileB ~/testfileC" "~/test/"
or do this:
proc my_scp_multi {ACCOUNT SERVER PW args} {
set timeout 30
send_user -- "\n"
set files [lrange $args 0 end-1]
set newfolder [lindex $args end]
spawn scp {*}$files $ACCOUNT#$SERVER:[esc $newfolder]
And then do not quote the filenames
my_scp_multi "ACCOUNT" "SERVER" "PW" ~/testfileA ~/testfileB ~/testfileC "~/test/"
The splat ({*}) splits the list up into it's individual elements so the spawn command sees several words, not a single word. See http://tcl.tk/man/tcl8.5/TclCmd/Tcl.htm
You could spawn a shell and then run the scp command instead:
spawn bash
send "scp $files $ACCOUNT#$SERVER:[esc $newfolder]\r"
This allows for glob expansion but adds extra housekeeping as you will need to trap when the scp process is completed, as you still have a shell running.
You could add below to your expect block:
-re "100%" {
if { $index < $count } {
set index [expr $index + 1]
exp_continue
}
}
Where index is the # of file being transferred and count the nr of files.
You should be using SSH public key authentication instead of typing in the password with expect. When it's set up properly, scp will work without any human input of passwords while keeping the system very secure. You will be free from all the troubles with expect.
How do I setup Public-Key Authentication?
http://www.ece.uci.edu/~chou/ssh-key.html
If there's some reason why you cannot use pubkey, you may find sftp useful because it accepts a batch command file as -b batchfile. See man 1 sftp Not a very good solution when expect can actually split the arguments