expect script not matching in bash - expect

I have function who is calling expect , but expect is not matching second line, it is matching first match match, I tried all sort of thinks, please help
function login_ssh_user {
logger $FUNCNAME "start"
if [ -z "${LDAPPWD}" ]; then
get_ldap_pwd
fi
echo "\n ${LDAPUSR} \n\n"
echo "\n ${LDAPPWD} \n"
echo "\n ${SOESYS} \n"
export U1="${LDAPUSR}"
export S1="${SOESYS}"
export P1="${LDAPPWD}"
expect << EOF
spawn ssh "$U1#$S1";
expect "password:" { send $P1\r }
expect "$ " { send_user "Login Success ... PASS\n"; send exit\r }
**expect "denied" { send_user "Login Failure ... Fail\n";** send "^C" }
EOF
logger $FUNCNAME "end"
}

Your code indicates you expect to see the password prompt, then you expect to see a shell prompt, then you expect to see Denied. Clearly you will either see the prompt or the denied message:
function login_ssh_user {
#...
export U1="${LDAPUSR}"
export S1="${SOESYS}"
export P1="${LDAPPWD}"
expect << 'EOF'
spawn ssh $env(U1)#$env(S1)
expect "password:" { send $env(P1)\r }
expect {
"$ " { send_user "Login Success ... PASS\n"; send exit\r }
denied" { send_user "Login Failure ... Fail\n"; send \x03 }
}
EOF
logger $FUNCNAME "end"
}
Notes:
I've quoted the EOF in the redirection. That effectively single quotes the whole expect script body
you export your variables, so expect can fetch them out of the env array
both the shell prompt and "denied" are branches in a single expect command. Whichever one expect sees first will "win".
Ctrl+C is \x03

Related

Conditions in Expect script causing it to hang

I'm using an expect script (within a bash script) to ssh into a remote server and execute a script, the local server will know that the remote server's script is finished as it (remote) will echo "finished" at the end of execution.
However, the script can be in one of three locations, so I've designed the following expect script, although in the code example it successfully executes the remote server script - it's unable to detect the printed "finished" and consequently hangs.
Local Server Expect Script:
versionScriptLoc1="/path/to/script1"
versionScriptLoc2="/path/to/script2"
versionScriptLoc3="/path/to/script3"
expect <<-EOS |& tee logfile.${hostname}.log
#!/usr/bin/expect
set timeout $EXP_TIMEOUT
puts "\nLogging into remote host via SSH..\n"
spawn ssh -q -tt -o ConnectTimeout=$SSH_TIMEOUT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${hostname}
expect "*assword*"
send -- "$secret\r"
expect {
"*assword*" {
send \x03
puts "\nIncorrect Password\n"
exit 1
}
"$prompt" {
send -- "/usr/seos/bin/sesu - $user\r"
expect "*assword*"
send -- "$secret\r"
expect "$prompt"
send -- "${versionScriptLoc1}\r"
expect {
"finished" {
}
"No such file or directory" {
puts "Location 1 execution Failed"
send -- "${versionScriptLoc2}\r"
expect {
"No such file or directory" {
puts "Location 2 execution Failed"
send -- "${versionScriptLoc3}\r"
}
}
exp_continue
}
}
send -- "exit\r"
expect "$prompt"
send -- "exit\r"
}
}
expect eof
exit 0
EOS
I thought exp_continue would make the expect loop and look for "finished". In the above code, the script is located in location 2, it sucessfully triggers but even though "finished" is printed, it still hangs.
Any help in why it's hanging/ potentially a better design would be highly appreciated.
PS: I know I should be spawning the scripts instead of triggering them like a user, but I've tried in the past and it didn't work.
I'd recommend you don't write your expect code so deeply nested.
The problem is that, after running script2, you hit the exp_continue whether
you're successful or not. Too bad expect doesn't have an exp_break or a
goto.
So a bit of restructuring is needed. I'm using some boolean variables to
manage the control flow.
Since I've introduces some Tcl variables, it will make separating the shell
vars from the tcl vars more difficult. We'll turn the shell vars into
environment vars, and quote the heredoc:
# ...
export EXP_TIMEOUT SSH_TIMEOUT
export hostname user secret prompt
export versionScriptLoc1 versionScriptLoc2 versionScriptLoc3
expect <<'EOS' 2>&1 | tee "logfile.${hostname}.log"
set timeout $env(EXP_TIMEOUT)
puts "\nLogging into remote host via SSH..\n"
spawn ssh -q -tt -o ConnectTimeout=$env(SSH_TIMEOUT) -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $env(hostname)
expect "*assword*"
send -- "$env(secret)\r"
expect {
"*assword*" {
send \x03
puts "\nIncorrect Password\n"
exit 1
}
"$env(prompt)"
}
send -- "/usr/seos/bin/sesu - $env(user)\r"
expect "*assword*"
send -- "$env(secret)\r"
expect "$env(prompt)"
set run2 false
send -- "$env(versionScriptLoc1)\r"
expect {
"No such file or directory" {
puts "Location 1 execution Failed"
set run2 true
}
"finished"
}
set run3 false
if {$run2} {
send -- "$env(versionScriptLoc2)\r"
expect {
"No such file or directory" {
puts "Location 2 execution Failed"
set run3 true
}
"finished"
}
}
if {$run3} {
send -- "$env(versionScriptLoc2)\r"
expect {
"No such file or directory" {
puts "Location 3 execution Failed"
}
"finished"
}
}
send -- "exit\r"
expect "$env(prompt)"
send -- "exit\r"
expect eof
exit 0
EOS

How can I return an error in bash expect?

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.

expect statement in bash script for error validation of expected output

I have the following expect statement within my bash script:
/usr/bin/expect << EOF
spawn -noecho lucli users add -username user -role admin -email
user#user.com
expect "password:" { send "password\n" }
expect "password:" { send "password\n" }
expect eof
EOF
I want the expect script to validate that the correct output is returned from the CLI command after it passes the passwords and creates the user.
The message I want to validate that gets returned is "added to the system successfully"
I can't figure out how to do that from within the bash script using expect.
Can anyone help?
You could try something like this:
# note the quoted here-doc word
status=$(/usr/bin/expect << 'EOF'
spawn -noecho lucli users add -username user -role admin -email
user#user.com
expect "password:" { send "password\r" }
expect "password:" { send "password\r" }
expect eof
set status [string match "*added to the system successfully*" $expect_out(buffer)]
# $status will be the C-like boolean 0 or 1
exit $status
EOF
)
if [[ $status -eq 1 ]]; then
echo "user added OK"
else
echo "user not added"
fi
ref: https://tcl.tk/man/tcl8.6/TclCmd/string.htm

scp command not working inside expect in bash script

while [ $FileLine -le $FileListLines ];
do
# extract each line from FileList
str=$(tail -n+$FileLine ../$FileList | head -n1)
hostpath=$username#$ip:$str
export hostpath ip
expect -c '
spawn bash -c "scp -pr $env(hostpath) $env(ip)"
expect {
"(yes/no)?"{
send "yes\r"
expect "*?assword:*"
send "password\r"
}
"*?assword:*"{
send "password\r"
}
}
'
FileLine=$(( $FileLine + 1 ))
done
The above is a part of a bash script. The scp command in the expect block is not working, that is, files from the remote machine are not getting copied to the local machine.
The same scp command with the path and hostname is working fine when being run from the terminal.
Add expect eof at the end of the expect code otherwise the scp process would be killed right after the password is sent. (Also add a space between the pattern and { in the expect {} block though not sure if that's a problem.)
expect -c '
spawn bash -c "scp -pr $env(hostpath) $env(ip)"
expect {
"(yes/no)?" {
send "yes\r"
expect "*?assword:*"
send "password\r"
}
"*?assword:*" {
send "password\r"
}
}
expect eof
'
UPDATE
Just tried and "(yes/no)?"{ would not work. The space between the pattern and { is required so it should be "(yes/no)?" {.

how to check the file on remote host is exsit with shell script

i want check some files on remote host are exsit with shell script,for my local machine and remote host are not be trusted with each other,so i use expect in my script,here are my code
expect << EOF
spawn ssh $src_user#$src_host "test -f $src_pub || echo CheckFalse "
expect {
"yes/no*" {
send "yes\n"
}
"$src_host's password:" {
send "$src_passwd\n"
}
eof { exit }
}
expect CheckFalse { exit 11 }
EOF
if [ $? -ne 11 ];then
echo "file is exsit!"
else
echo "file is not exsit!"
fi
Use ssh with a command (using -c).
ssh otherhost -c 'ls /path/ filename'
And parse the output as you wish
There are a few issues with your script
Using test will only check if the file doesn't exist, rather use ls which gives output in both conditions and is easier to work with in this case.
You should use exp_continue after sending the authenticity check and the password so the expect loop can continue from where it left of from the previous match.
Add checks for $src_pub and 'No such file' in your expect block to trap for both conditions as shown below:
Try below:
spawn ssh $src_user#$src_host "ls $src_pub"
expect {
"yes/no*" {
send "yes\n"
exp_continue
}
-re "(.*)assword:" {
send -- "$src_passwd\n"
}
$src_pub {
exit 0;
}
-re "(.*) No such" {
exit 1;
}
}

Resources