How to get expect to set a bash variable - bash

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}

Related

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.

how can we use expect command inside if loop in shell script

I have a set of commands to install my product. The installation process shoots up a few prompts. The problem is we have 2 versions of the product and the prompts are different during installation of each of these.
I have put a if condition and added expect inside it. But the script keeps failing.
#!/bin/bash
#In this mode any command this script runs which returns a non-zero
exitcode - an error in the world of shell - will cause your script to
itself terminate immediately with an error.
set -e
set -x
#Below varibles is used to set flag '0=Stage Pass' and '1=Stage Fail'
f="Fail" gotoStage="Pass" sdiclistage="Pass"
# An error exit function
error_exit()
{
echo "$1" 1>&2
exit 1
}
# Using error_exit : Goto data insight folder
if cd /opt/datainsight/; then
echo "inside /opt/datainsight directory."
else
{
error_exit "Cannot change directory! Aborting."
gotoStage=$f;
}
fi
echo "Worker ip: $1"
echo "NMS ip: $2"
echo "DI tar name: $3"
echo "DI version: $4"
echo "executing sdi-cli cluster command"
if [[ "1.5.3" == "1.5"* ]];
then {
echo "inside compa"
/usr/bin/expect <<EOD
set timeout 600
spawn sdi-cli cluster provision -y
expect {
timeout { send_user "\nFail to get password promt\n"; exit
1 }
"password for pwd:"
}
send "pwd\r";
expect {
timeout { send_user "\n Fail to continue connecting \n";
exit 1}
"Are you sure you want to continue connecting"
}
send "yes\r"
expect {
timeout { send_user "\n Fail to get second password promt
\n"; exit 1}
"password for pwd:"
}
send "pwd\r"
expect {
timeout { send_user "\n Error for completing provision\n";
exit 1}
"Provisioning complete."
}
EOD
echo "\nOutside sdi-cli cluster provision\n";
}
else
{
echo "In else"
/usr/bin/expect <<EOD
set timeout 600
spawn sdi-cli cluster provision -y
expect {
timeout { send_user "\n Fail to continue connecting
\n"; exit 1}
"Are you sure you want to continue connecting"
}
send "yes\r"
expect {
timeout { send_user "\n Error for completing provision\n"; exit
1}
"Provisioning complete."
}
EOD
echo "\nOutside sdi-cli cluster provision\n";
}
fi
if [ $? = 1 ]; then
{
echo "\nError for executing sdi-cli script\n";
sdiclistage=$f;
}
else
{
echo "\n sdi-cli script executed successfully\n";
}
fi
if [ $gotoStage = "Pass" ] && [ $sdiclistage = "Pass" ] ; then
{
echo "PASS" >> result.txt
echo "\n All installation stages Passed\n ";
}
else
{
echo "FAIL" >> result.txt
echo "\n All/Some installation stages failed \n";
}
fi
sleep 300 #5min
ui=$(kubectl get pods |grep ui |awk '{print $1}') #get ui pod name
kubectl exec -it $ui cat GITINFO.json >>buildinfo.txt
echo "\n Build info file created successfully";
This always fails with
echo 'DI version: 1.5.3'
+ echo 'executing sdi-cli cluster command'
./di_install14_deepika.sh: line 134: warning: here-document at line 41 delimited by end-of-file (wanted `EOD')
./di_install14_deepika.sh: line 135: syntax error: unexpected end of file

Get the result of remote script in Expect

Im executing the remote script and checking return status of the script but if I do in the following way its returning the status of the password but not the status of the called script.How can I get the return status of the called script. Please help thanks in advance.
#!/usr/bin/expect
proc auto { } {
global argv
set timeout 120
set ip XXXX.XXX.XX.XX
set user name
set password pass
set ssh_opts {-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no}
set script /path-to-script/test.sh
spawn ssh {*}$ssh_opts $user#$ip bash $script {*}$argv
expect "Password:"
send "$password\r"
send "echo $?\r"
expect {
"0\r" { puts "Test passed."; }
timeout { puts "Test failed."; }
}
expect eof
}
auto {*}$argv
You're automating ssh bash remote_script, so you're not going to get a shell prompt where you can echo $? -- ssh will launch your script and then exit.
What you need to do is get the exit status of the spawned process (ssh is supposed to exit with the remote command's exit status). expect's wait command gets you the exit status (among other info)
spawn ssh {*}$ssh_opts $user#$ip bash $script {*}$argv
expect {
"Password:" { send "$password\r"; exp_continue }
timeout { puts "Test failed." }
eof
}
# ssh command is now finished
exp_close
set info [wait]
# [lindex $info 0] is the PID of the ssh process
# [lindex $info 1] is the spawn id
# [lindex $info 2] is the success/failure indicator
if {[lindex $info 2] == 0} {
puts "exit status = [lindex $info 3]"
} else {
puts "error code = [lindex $info 3]"
}

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;
}
}

bash + expect, running in background

I'm using expect to establish a persistent ssh connection
set stb_ip [lindex $argv 0]
spawn -noecho ssh -o ControlMaster=auto -o ControlPath=/tmp/ssh-master-%r#%h:%p -o ConnectTimeout=1 -O exit root#$stb_ip
spawn -noecho ssh -fN -o ControlMaster=yes -o ControlPath=/tmp/ssh-master-%r#%h:%p -o ControlPersist=360 -o ConnectTimeout=1 root#$stb_ip
expect {
-re ".*password:" {send "\r"; interact}
}
Unfortunately I can't manage to put this into background, I triend expect_background, fork+disconect but no luck.
Even triend running this from another script with
excpect -f script.ex param1 param2 &
but with no luck. Any help ?
Heres a proc you can use to login and then interact. I have not tried it with all the ssh Options but I don't see any reason it would not work. Since I use the 8.6 command "try" this is for 8.6 tcl only but you can modify the try to use catch for earlier versions pretty easily.
#!/bin/sh
# the next line restarts using wish \
exec /opt/usr8.6b.5/bin/tclsh8.6 "$0" ${1+"$#"}
if { [ catch {package require Expect } err ] != 0 } {
puts stderr "Unable to find package Expect ... adjust your auto_path!";
}
proc login { user password cmdline } {
set pid [spawn -noecho {*}$cmdline ]
set bad 0;
set done 0;
exp_internal 0; # set to one for extensive debug
log_user 0; # set to one to watch action
set timeout 10
set passwdcount 0
set errMsg {}
# regexp to match prompt after successfull login you may need to change
set intialpromptregexp {^.*[\$\#>]}
expect {
-i $spawn_id
-re $intialpromptregexp {
send_user $expect_out(0,string);
set done 1
}
-re {.*assword:} {
if { $passwdcount >= 1 } {
lappend errMsg "Invalid username or password for user $user"
set bad 1
} else {
exp_send -i $spawn_id "$password\r"
incr passwdcount
exp_continue;
}
}
-re {.*Host key verification failed.} {
lappend errMsg "Host key verification failed."
set bad 1
}
-re {.*onnection refused} {
lappend errMsg "Connection Refused"
set bad 1
}
-re {.*onnection closed by remote host} {
lappend errMsg "Connection Refused"
set bad 1
}
-re {.*Could not resolve hostname (.*): Name or service not known} {
lappend errMsg "Host invalid: Could not resolve hostname in $cmdline : Name or service not known"
set bad 1
}
-re {\(yes/no\)\?} {
exp_send -i $spawn_id "yes\r"
exp_continue;
}
timeout {
lappend errMsg "timeout \[[expr { [clock seconds] - $start } ]\]"
set bad 1
}
fullbuffer {
lappend errMsg " buffer is full"
exp_continue;
}
eof {
puts "Eof detected "
set bad 1
set done 1 ;
}
}
if { $bad } {
throw CONNECTION_ERROR [join $errMsg \n ]
}
return $spawn_id
}
# get login information in somehow in this case from command line
set user [lindex $argv 0]
set passwd [lindex $argv 1]
set host [lindex $argv 2 ]
try {
set spawn_id [login $user $passwd "ssh -X $user#$host" ]
} trap CONNECTION_ERROR a {
puts "CONNECTION ERROR: $a"
exit 1
}
interact
set exitstatus [ exp_wait -i $spawn_id ];
catch { exp_close -i $spawn_id };
# more clean up here if you want
Assuming your script works in the "foreground"...
nohup expect -f script.ex param1 param2 &
Here's a script I made a long time ago. It does what you want but doesn't use Expect (which I loathe). I don't use it any more, I can't guarantee that it even still works but it should get you going.
#!/bin/sh
#
# Persistent ssh: Automatically create persistent ssh connections using OpenSSH 4.0
[ -z "$USER" ] && USER=`whoami`
MASTERSOCKDIR="/tmp/pssh-$USER"
MASTERSOCK="$MASTERSOCKDIR/%r-%h-%p"
# Check if master is running
output=`ssh -o ControlPath="$MASTERSOCK" -O check "$#" 2>&1`
if [ $? -ne 0 ]; then
case "$output" in
Control*)
# Master not running, SSH supports master
# Figure out socket filename
socket=`echo "$output" | sed -n -e 's/[^(]*(\([^)]*\)).*/\1/p' -e '1q'`
# Clean old socket if valid filename
case "$socket" in
"$MASTERSOCKDIR"/*) rm -f "$socket" >/dev/null 2>&1 ;;
esac
# Start persistent master connection
if [ ! -d "$MASTERSOCKDIR" ]; then
mkdir "$MASTERSOCKDIR"
chmod 700 "$MASTERSOCKDIR"
fi
ssh -o ControlPath="$MASTERSOCK" -MNf "$#"
if [ $? -ne 0 ]; then
echo "$0: Can't create master SSH connection, falling back to regular SSH" >&2
fi
;;
*)
# SSH doesn't support master or bad command line parameters
ERRCODE=$?
echo "$output" >&2
echo "$0: SSH doesn't support persistent connections or bad parameters" >&2
exit $ERRCODE
;;
esac
fi
exec ssh -o ControlPath="$MASTERSOCK" -o ControlMaster=no "$#"
To execute an expect script in the background use expect eof at the end of your expect script. In case you have defined interact remove it from your script.
Changed script of OP
set stb_ip [lindex $argv 0]
spawn -noecho ssh -o ControlMaster=auto -o ControlPath=/tmp/ssh-master-%r#%h:%p -o ConnectTimeout=1 -O exit root#$stb_ip
spawn -noecho ssh -fN -o ControlMaster=yes -o ControlPath=/tmp/ssh-master-%r#%h:%p -o ControlPersist=360 -o ConnectTimeout=1 root#$stb_ip
expect {
-re ".*password:" {send "\r"; interact}
}
expect eof
An other example [1].
#!/usr/bin/expect -f
set host "host"
set password "password"
spawn ssh $host
expect {
"(yes/no)?" {
send -- "yes\r"
exp_continue
}
"*password:*" {
send -- "$password\r"
}
}
##Removing this:
#interact
##And adding this:
expect eof
exit

Resources