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.
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
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.
I have a problem, if I run a TCL/expect script from cygwin.
When I'm getting the 64th session, I got the following error(debug output):
spawn ssh useri#1.1.1.1
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {8972}
expect: does "" (spawn_id exp64) match glob pattern "unsuccessful login"? no
"Can't ping useri#1.1.1.1! Quitting."? no
"refused"? no
"Bad IP address"? no
"computer name"? no
"Usage of telnet deprecated, instead use"? no
"Invalid input detected at"? no
"timed out"? no
"o route to host"? no
"closed"? no
"ermission denied"? no
"estination unreachable"? no
"imeout expired"? no
"(NO/YES)"? no
"continue connecting"? no
"Username: useri"? no
"ser:"? no
"sername:"? no
"assword:"? no
expect: does "" (spawn_id exp64) match glob pattern "useri#server%"? no
"useri#server:*%"? no
"useri#server>"? no
"useri#server"? no
"server "? no
"server\r"? no
"server>"? no
"server"? no
expect: does "" (spawn_id exp64) match glob pattern "#asd"? no
expect: read eof
expect: set expect_out(spawn_id) "exp64"
expect: set expect_out(buffer) ""
exp_i_parse_states: : spawn id exp64 not open: spawn id exp64 not open
while executing
"expect {
# Special cases, switch to different login
-i $sid "unsuccessful login" { exp_continue }
-i $sid "Can't ping $Username#$Ho..."
(procedure "connecthor" line 5)
invoked from within
"connecthor $Servername($i) $Server($i) $Serveruser($i) $Serverpass($i) $Option $sid $Enablepass"
(procedure "dialin" line 44)
invoked from within
"dialin $Routername $L_user"
("-(checker)" arm line 31)
invoked from within
"switch -regexp -nocase -- [lindex $argv 0] \
-(initpass) {
puts "\r\rThis is the initial password manager module of to script."
puts "WARNIN..."
(file "/usr/bin/to" line 366)
The script is working fine, doing it's job until it reaches the session exp64. I've also tried to run the script under Ubuntu natively instead of Cygwin. It was working fine, without any error.
I'm experiencing this issue with other scripts as well.
I've already tried to re-install Cygwin, same issue appeared.
Do you maybe have an idea what could it be?
In the meantime:
Just wrote a minimal script, SSHing to a Cisco router, send a command, and exit out. It does the job in an endless loop, so I can log the $spawn_id in the meantime:
#!/usr/bin/expect
set i 1
while {1} {
spawn ssh username#1.1.1.1
set sid $spawn_id
expect "assword:"
send "Start123\r"
expect "Router"
send "show ip int brief\r"
while {1} {
expect {
"Router" {send "exit\r"}
eof {break}
}
}
puts "*** Loop is running: $i, spawn_id is: $sid ***"
incr i
}
I get the same error with this as well:
*** Loop is running: 60, spawn_id is: exp63 ***
spawn ssh username#1.1.1.1
send: spawn id exp64 not open
There's a limit on the number of simultaneous expect sessions that can be maintained. That limit varies by OS, and is (on some platforms at least) a global limit, not a process-local limit. Though since you're on Windows, there might also be per-process limits that you're hitting; I'm not entirely sure (though 64 sounds exactly like one of those). In any case, it sounds like you're hitting resource exhaustion.
Are you remembering to close once you're finished interacting with a particular host? If you've got lots of old expect sessions about in the process, you'll be leaking a pretty precious pool of resources.
The problem is that you did not wait the exited ssh process. Without the wait, an exited ssh process would be showed as defunct which is still consuming the PID and maybe some FDs. You can try like this:
while {1} {
expect {
"Router" {send "exit\r"}
-> eof { wait -nowait; break }
}
}
puts "*** Loop is running: $i, spawn_id is: $sid ***"
incr i
or
while {1} {
expect {
"Router" {send "exit\r"}
eof { break }
}
}
-> wait -nowait
puts "*** Loop is running: $i, spawn_id is: $sid ***"
incr i
I'm having some trouble with an expect script I am writing. The goal of the script is to SSH to a partner and obtain network configuration information. That information is saved in a file and copied back to the local host for processing. The file is then removed from the remote host upon exit.
Under normal conditions, everything works great and as I would expect. However, should the user press ^C to interrupt the script during the SCP process, the file is not removed from the remote host. It is left there. I am not sure why this is or how to force its removal. When stepping through my signal handler and exit handler during such a case, it appears that for some reason the exit handler doesn't think that the file exists and skips the if statement. I have no idea why, though.
I have copied the exit handler and signal handler from my script below. Please let me know if you have any ideas or see any obvious errors. I'm not sure where to go from here.
# Configure exit handler
exit -onexit {
# Remove configuration info file if it exists
if {[file exists ptinit.txt]} {
send "rm -rf ptinit.txt\r"
expect -exact "rm -rf ptinit.txt\r"
}
}
# Configure signal trap to remove partner file before exiting
proc errsig_handler {pidlst} {
send_user "\nStop command received from user. Exiting.\n"
for {set i [expr [llength $pidlst]-1]} {$i >= 0} {incr i -1} {
send_user "Current PID is: [lindex $pidlst $i]\n"
# If pid is not null and process is currently running, kill it
if {[lindex $pidlst $i] != "" && [exec kill -0 [lindex $pidlst $i] 2>/dev/null] == ""} {
send_user "PID [lindex $pidlst $i] is not null and is currently running.\n"
exec kill -9 [lindex $pidlst $i]
}
}
}
trap {errsig_handler $cur_pid} {INT TSTP}
UPDATE:
I tried the method suggested by Dinesh, but am still having issues. I updated the exit handler code as follows:
exit -onexit {
exp_internal 1
unset expect_out(buffer)
# Remove configuration info file from remote server
send_user "\nIN EXIT HANDLER\n"
send "rm -rf ptinit.txt\r"
expect {
"rm -rf ptinit.txt\r" {
sleep 5
send "exit\r"
expect eof {puts "EOF in rm match received."; sleep 2}
}
"cannot remove" {
puts "File deletion was not successful - ptinit.txt needs to be deleted manually."
}
-re $prompt {
sleep 5
send "exit\r"
expect eof {puts "EOF received."; sleep 2}
}
}
send_user "LEAVING EXIT HANDLER\n"
}
The only way I could get this to work was by commenting out my signal handler for loop that kills the spawned PIDs. When it is uncommented, the exit handler expect times out.
Either way, the file still is not removed from the remote system. This is what I see from exp_internal 1:
Stop command received from user. Exiting.
IN EXIT HANDLER
send: sending "rm -rf ptinit.txt\r" to { exp9 }
Gate keeper glob pattern for '(%|#|>|\$) $' is ''. Not usable, disabling the performance booster.
expect: does " \r\n" (spawn_id exp9) match glob pattern "rm -rf ptinit.txt\r"? no
"cannot remove"? no
"(%|#|>|\$) $"? (No Gate, RE only) gate=yes re=no
expect: does " \r\nrm -rf ptinit.txt\r\n" (spawn_id exp9) match glob pattern "rm -rf ptinit.txt\r"? yes
expect: set expect_out(0,string) "rm -rf ptinit.txt\r"
expect: set expect_out(spawn_id) "exp9"
expect: set expect_out(buffer) " \r\nrm -rf ptinit.txt\r"
send: sending "exit\r" to { exp9 }
expect: read eof
expect: set expect_out(spawn_id) "exp9"
expect: set expect_out(buffer) "\n\rptinit.txt 0% 0 0.0KB/s --:-- ETA\rptinit.txt
100% 35 0.0KB/s 00:00 \r\nexit\r\n"
EOF in rm match received.
LEAVING EXIT HANDLER
tty_set: raw = 5, echo = 0
UPDATE 2:
I updated the exit handler to the following:
exit -onexit {
exp_internal 1
unset expect_out(buffer)
# Remove configuration info file from remote server
set filename ptinit.txt
send_user "\nIN EXIT HANDLER\n"
send "rm -rf $filename\r"
expect {
"cannot remove" { puts "File deletion is not successful" }
-re $prompt { puts "File deleted" }
}
send_user "LEAVING EXIT HANDLER\n"
}
This is the debugging information - still no file deleted.
Stop command received from user. Exiting.
IN EXIT HANDLER
send: sending "rm -rf ptinit.txt\r" to { exp9 }
Gate keeper glob pattern for '(%|#|>|\$) $' is ''. Not usable, disabling the performance booster.
expect: does " \r\n" (spawn_id exp9) match glob pattern "cannot remove"? no
"(%|#|>|\$) $"? (No Gate, RE only) gate=yes re=no
expect: does " \r\nrm -rf ptinit.txt\r\n" (spawn_id exp9) match glob pattern "cannot remove"? no
"(%|#|>|\$) $"? (No Gate, RE only) gate=yes re=no
expect: does " \r\nrm -rf ptinit.txt\r\n\rptinit.txt 0% 0 0.0KB/s --:-- ETA" (spawn_id
exp9) match glob pattern "cannot remove"? no
"(%|#|>|\$) $"? (No Gate, RE only) gate=yes re=no
expect: does " \r\nrm -rf ptinit.txt\r\n\rptinit.txt 0% 0 0.0KB/s --:-- ETA\rptinit.tx
t 100% 35 0.0KB/s 00:00 \r\n" (spawn_id exp9) match glob pattern "cannot remove"? no
"(%|#|>|\$) $"? (No Gate, RE only) gate=yes re=no
expect: read eof
expect: set expect_out(spawn_id) "exp9"
expect: set expect_out(buffer) " \r\nrm -rf ptinit.txt\r\n\rptinit.txt 0% 0 0.0KB/s --
:-- ETA\rptinit.txt 100% 35 0.0KB/s 00:00 \r\n"
LEAVING EXIT HANDLER
tty_set: raw = 5, echo = 0
The problem is because of file exists. It checks the file path present or not in the local machine wherever you are running the Expect script, not in the remote directory.
Removing that will solve the problem.
#This is a common approach for few known prompts
#If your device's prompt is missing here, then you can add the same.
set prompt "#|>|\\\$"; # We escaped the `$` symbol with backslash to match literal '$'
set filename ptinit.txt
send "rm -rf $filename\r"
expect {
"cannot remove" { puts "File deletion is not successful"}
-re $prompt { puts "File deleted" }
}
Update :
In your code, you are expecting for rm -rf ptinit.txt\r which is wrong. Because expect will see the what is sent to the spawned process (that is also rm -rf ptinit.txt\r) and match that. Due to this, it is never actually sent to the spawned process.
So, Can you enable debug for the above code given by me and share here?
I am trying to execute a script that executes an EXPECT script and a spawned process which has exit code in it. But I'm unable to get the exit code of the spawned process to main script. I'm always getting zero as success.
expect script is :
[Linux Dev:anr ]$ cat testexit.sh
#!/bin/bash
export tmp_script_file="/home/anr/tmp_script_temp.sh"
cp /home/anr/tmp_script $tmp_script_file
chmod a+x $tmp_script_file
cat $tmp_script_file
expect << 'EOF'
set timeout -1
spawn $env(tmp_script_file)
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
}
EOF
echo "spawned process status" $?
rm -f $tmp_script_file
echo "done"
Spawned script:
[Linux Dev:anr ]$ cat tmp_script
exit 3
Execution of Expect script:
[Linux Dev:anr ]$ ./testexit.sh
exit 3
spawn /home/anr/tmp_script_temp.sh
spawned process status 0
done
Problem is I am unable to get the spawned exit return code to expect script. I want the exit code 3 of spawned script to main script and main script should be exit with exit code 3.
Please help me to get the spawned exit code to main script.
You get the exit status of the spawned process with the wait command:
expect <<'END'
log_user 0
spawn sh -c {echo hello; exit 42}
expect eof
puts $expect_out(buffer)
lassign [wait] pid spawnid os_error_flag value
if {$os_error_flag == 0} {
puts "exit status: $value"
} else {
puts "errno: $value"
}
END
hello
exit status: 42
From the expect man page
wait [args]
delays until a spawned process (or the current process if none is named) terminates.
wait normally returns a list of four integers. The first integer is the pid of the process that was waited upon. The second integer is the corresponding spawn id. The third integer is -1 if an operating system error occurred, or 0 otherwise. If the third integer was 0, the fourth integer is the status returned by the spawned process. If the third integer was -1, the fourth integer is the value of errno set by the operating system. The global variable errorCode is also set.
Change
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
}
to
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
eof
}
Then add the lassign and if commands.
With the help of glenn, I got solution.. and my final script is::
expect script is
[Linux Dev:anr ]$ cat testexit.sh
#!/bin/bash
export tmp_script_file="/home/anr/tmp_script_temp.sh"
cp /home/anr/tmp_script $tmp_script_file
chmod a+x $tmp_script_file
cat $tmp_script_file
expect << 'EOF'
set timeout -1
spawn $env(tmp_script_file)
expect {
"INVALID " { exit 4 }
timeout { exit 4 }
eof
}
foreach {pid spawnid os_error_flag value} [wait] break
if {$os_error_flag == 0} {
puts "exit status: $value"
exit $value
} else {
puts "errno: $value"
exit $value
}
EOF
echo "spawned process status" $?
rm -f $tmp_script_file
echo "done"
Spawned script:
[Linux Dev:anr ]$ cat tmp_script
exit 3
Execution of Expect script:
[Linux Dev:anr ]$ ./testexit.sh
exit 3
spawn /home/anr/tmp_script_temp.sh
exit status: 3
spawned process status 3
done
Thanks Glenn once again..
After struggling few days with expanding variable inside the expect heredoc, finally i came across an another approach i thought may be helpful to someone in need. My requirement was to pass command and password to a shell function, execute the command in remote host as part of expect heredoc and get the return exit code.
Example:
function shell_function {
# Get the command and password as arguments
# Run command using expect
# Return the exit code
}
shell_function <cmd> <password>
echo $?
Like everyone else expanding of variable inside the heredoc was a problem, which required exporting the value into an environment variable and use env to get the variable inside heredoc. Since, password was one of the arguments i didn't want to store it as part of an environment variable. So, instead of enclosing heredoc opening with single quotes, the variables of heredoc have been escaped. This allowed the direct usage of arguments passed.
Following is the final script:
#! /bin/bash
# This function runs a command like 'ssh' and provides the password
function run_with_password {
cmd="$2"
paswd="$1"
expect << END
set timeout 60
spawn $cmd
expect {
"yes/no" { send "yes\r" }
"*assword*" { send -- $paswd\r }
}
expect EOF
catch wait result
exit [lindex \$result 3]
END
}
my_password="AnswerIS42Really?"
cmd_to_run="ssh userid#hostname"
cmd_to_run="$cmd_to_run ls .sawfish"
run_with_password $my_password "$cmd_to_run"
echo "Command run code: $?"
In the above code the escaped expect variable is $result. After changing the variable to \$result, the script started working like charm.
My sincere thanks to users who have provided answers to following questions, which served as a stepping stones to reach my solution.
Douglas Leeder: help with expect script, run cat on remote comp and get output of it to the variable
glenn jackman: How to return spawned process exit code in Expect script?