Why does this expect script continually spawn the ssh command, never printing "going to sleep" or "out of sleep", and never sleeping?
My intention is to try ssh'ing, if it sees "password: " to exit the while loop (more code not seen here would include an interact.) If 3 seconds go by, or ssh dies before then, it should puts, sleep for 3 seconds, puts again, and try ssh again.
The hostname "doesntexist" is used to force a failure, such as Name or service not known.
#!/usr/bin/expect -f
set readyForPassword 0
while { $readyForPassword == 0 } {
spawn ssh nouser#doesntexist
expect -timeout 3 {
"password: " {
set readyForPassword 1
} timeout {
puts "going to sleep"
sleep 3
puts "out of sleep"
} eof {
puts "going to sleep"
sleep 3
puts "out of sleep"
}
}
}
When -timeout flag is used, it should be prior to the Expect's pattern, not on the actions.
By debugging, we can find that the pattern being taken by Expect with your existing code is,
expect: does "" (spawn_id exp6) match glob pattern "\n "password: " {\n set readyForPassword 1\n } timeout {\n puts "going to sleep"\n sleep 3\n puts "out of sleep"\n } eof {\n puts "going to sleep"\n sleep 3\n puts "out of sleep"\n }\n "? no
From the 76th page of the Exploring Expect book, we can see the below statements,
The initial open brace causes Tcl to continue scanning additional
lines to complete the command. Once the matching brace is found, all
of the patterns and actions between the outer braces are passed to
expect as arguments
What went wrong then ?
The -timeout is not an action, but just a flag. Expect assumed the following as the pattern
"password: " {
set readyForPassword 1
} timeout {
puts "going to sleep"
sleep 3
puts "out of sleep"
} eof {
puts "going to sleep"
sleep 3
puts "out of sleep"
}
Remember, Expect does not mandate the action, only the pattern i.e. It will act as if we like only pattern is given, but no action to be taken.
Simply put, you code is as equivalent as
expect "Hello"; # Either 'timeout' or pattern can be matched. But, no action at all
Your code should be re-arranged as,
#!/usr/bin/expect -d
set readyForPassword 0
while { $readyForPassword == 0 } {
spawn ssh nouser#doesntexist
expect {
-timeout 3 "password: " {set readyForPassword 1}
timeout {
puts "going to sleep in timeout"
sleep 3
puts "out of sleep in timeout"
} eof {
puts "going to sleep in eof"
sleep 3
puts "out of sleep in eof"
}
}
}
Related
I want to check if there is an interface in the output of the command:
ip a
If there is not, then I want to stop the execution. I've tried this code:
#!/usr/bin/expect -f
spawn bash
set spi_bash $spawn_id
send "ip a\r"
expect {
-re "docker0" {puts "docker0 is there\n"; exp_continue }
timeout {puts "no docker0\n"; exit 1}
}
exit
but exp_continue continues to timeout case.
I'd suggest you don't need expect for this:
set output [exec ip a]
if {[string first docker0 $output] != -1} {
puts "no docker0"
}
But if this is part of a larger expect program, you can write
spawn bash
set spi_bash $spawn_id
# a regex matching your prompt: a dollar sign, a space, and end of input
# adjust as required
set prompt {\$ $}
send "ip a\r"
set has_docker false
expect {
-re "docker0" {set has_docker true; exp_continue}
-re $prompt
}
send "exit\r"
expect eof
if {$has_docker}
puts "docker0 is there"
} else {
puts "no docker0"
}
exit [expr {!$has_docker}]
I have a bash expect script like this, that I use for some operations on Jamf:
spawn firmwarepasswd -setpasswd
expect {
"Enter password:" {
send "$oldpass\r"
exp_continue
}
"Enter new password:" {
send "$newpass\r"
exp_continue
}
"Re-enter new password:" {
send "$newpass\r"
exp_continue
}
}
If the password fails, the script will not exit and jamf will keep trying to execute it. How can I get it to return and exit when the password is wrong?
I don't know Jamf, but I do have a little example for you:
function _cmd {
local cmd="${#?No command?}"
echo -ne "Testing $cmd\t: "
expect 2>&1 <<-EOF
set timeout -1
spawn ${cmd}
expect eof
catch wait result
exit [lindex \$result 3]
EOF
echo $?
}
function _ssh {
local status="${#?No command?}"
read -sp "remote password? " remote_pass
echo -ne "\nTesting ssh\t: "
expect 2>&1 <<-EOF
set timeout -1
spawn ssh $USER#127.0.0.1
expect {
"yes/no" { send "yes\r"; exp_continue }
"*password: " { send "${remote_pass}\r" }
}
expect "*#" { send "exit $status\r" }
expect eof
catch wait result
exit [lindex \$result 3]
EOF
echo $?
}
_cmd false
_cmd true
_ssh 3
exit 0
The last part after expect eof makes sure that the exit status is shared. The _ssh command will exit with status 3.
I was lurking on this site for quite awhile now because I am doing an SFTP in a expect/SH script. SSH keygen is not an option for us since we don't have access to the remote server so we're looking into using expect to provide password arg for SFTP.
Here is the script I am working on, and everything is working here except I want to capture or log to an output file if my transfer ("mput") completed successfully. Any advice on what code to put after the "mput" since if I add an expect_out(buffer) after, it is failing.
#!/bin/ksh
DIRROOT=/apps/gen/e2k/sys/bpp
COPYDIR=$DIRROOT/SENT
FILEHASH=TEST.SOME.FILE.*
if [ ! -f $COPYDIR/$FILEHASH ]; then
echo "No File"
fi
# New FTP credential from GIC
FTPSERV=**********
FTPUSER=**********
FTPPWD=**********
FTPDIR=/to-scs
/usr/local/bin/expect -f - <<EOFEXPECT1
#exp_internal 1
set timeout -1
set log [open "/dir/dir1/dir2/MIKETEST.txt" w]
spawn sftp -oPort=10022 $FTPUSER#$FTPSERV
expect "password:"
send "$FTPPWD\r";
expect "sftp> "
send "lcd $COPYDIR \r";
expect "sftp> "
send "cd /recipient \r";
expect "sftp> "
send "mput TEST.SOME.FILE.*\r";
put $log $expect_out(buffer)
close $log
expect "sftp> "
send "bye\r";
expect eof
EOFEXPECT1
if [ $? -eq 0 ]
then
echo "success"
else
echo "fail"
fi
You should be using puts instead of put and i wouldn't rely on $expect_out(buffer) for error checking. Rather use a nested expect statement to trap for common sftp scenarios/errors.
set timeout -1
send "mput TEST.SOME.FILE.*\r";
expect {
#Check for progress, note does not work with all versions of SFTP
#If a match is found restart expect loop
-re "\[0-9]*%" {
set percent $expect_out(0,string)
puts $logf "File transfer at $percent, continuing..."
exp_continue
}
#Check for common errors, by no means all of them
-re "Couldn't|(.*)disconnect|(.*)stalled" {
puts $logf "Unable to transfer file"
exit 1
}
#OK continue
"sftp>" {
puts $logf "File transfer completed"
}
}
Finally, i don't recommend using timeout of -1 (never) as this will lead to a stuck process sooner or later. Rather make us of a timeout value and trap for the possibility of a timeout to occur in the expect block.
set timeout 60
expect {
#Process timed out
timeout {
puts $logf "File transfer timed out"
}
}
I would like the following expect script to set a variable in the main bash script. In other words I would like to set $SSH_SUCCESS to a pass/fail string. I have tried using $expect_out and $::env(SSH_SUCCESS) but have been unsuccessful. How do I set a bash variable from expect?
expect << EOF
log_user 0
log_file $TEST_LOG
set timeout 5
spawn ssh root#$RADIO_IP
.....
....expect script, echoing the return of an SSH command...
send "echo\$?\n"
expect {
"0" {
send_user "SSH test: PASSED\r"
SSH_SUCCESS="PASSED"
}
"1" {
send_user "SSH test: FAILED\r"
SSH_SUCCESS="FAILED"
}
sleep 1
send_user "\n"
exit
EOF
echo $SSH_SUCCESS
I don't know Expect, but I think it's something like this
SSH_SUCCESS=$(expect <<EOF
...
expect {
"0" {
puts "PASSED"
}
"1" {
puts "FAILED"
}
...
EOF
)
echo $SSH_SUCCESS
There is no way to set a variable.
This is another way to determine the exit status of Expect.
expect << EOF
log_user 0
log_file $TEST_LOG
set timeout 5
spawn ssh root#$RADIO_IP
.....
....expect script, echoing the return of an SSH command...
send "echo\$?\n"
expect -re "\[0-9\]+$"
#set latest command exit status to valiable "exit_code"
set exit_status \$expect_out(0,string)
if { \$exit_status == 0 } {
send_user "SSH test: PASSED\r"
} else {
send_user "SSH test: FAILED\r"
}
sleep 1
send_user "\n"
# exit expect as latest command exit status
exit \$exit_status
EOF
if [ $? -eq 0 ];then
SSH_SUCCESS="PASSED"
else
SSH_SUCCESS="FAILED"
fi
echo ${SSH_SUCCESS}
Is it possible to run code when an expect script it terminated?
Given the following example;
#!/usr/bin/expect
while { true } {
puts "I am alive"
sleep 5
}
puts "I am dead"
This will continuously print "I am alive". When I press CTRL+C to kill the script, how can I call a function (or similar) to print "I am dead" on the way out?
This link explains how to handle SIGINT in Expect. This is what you want to do in your code:
#!/usr/bin/expect
proc sigint_handler {} {
puts "I am dead"
exit
}
trap sigint_handler SIGINT
while { true } {
puts "I am alive"
sleep 5
}