Expect Script Terminating on its own - expect

I have written the below expect script but it's not working as expected. I want the script to terminate automatically when all the commands are executed. However , the script either never terminates ( if set timeout -1 is used ) or terminates within seconds even before my commands are executed. Can someone please help ?
Here's the script :
#!/usr/local/bin/expect
spawn su vserve
set password vserve
set PWD whoami
set cmdstr(0) "bash /apps/vpn/vserve/vserve_profile"
set cmdstr(1) "bash /apps/vpn/asap/scripts/change_loopback.sh"
set timeout -1
expect "*Password:*" {
sleep 1
send "$password\r"
send "$PWD\r"
sleep 1
for {set i 0} {$i<[array size cmdstr]} {incr i} {
send "$cmdstr($i)\r"
}
send \"exit\r\"
expect eof
}

Usually in an interactive shell, you have to expect the specific shell prompt before you send next command. That's the way we make sure the previous command has really finished.

Related

Autoexpect hang at the end of the script

I'm running the following autoexpect script
#!/usr/bin/expect -f
set force_conservative 0 ;# set to 1 to force conservative mode even if
;# script wasn't run conservatively originally
if {$force_conservative} {
set send_slow {1 .1}
proc send {ignore arg} {
sleep .1
exp_send -s -- $arg
}
}
set timeout -1
spawn $env(SHELL)
match_max 100000
send -- "ssh f.olivieri#10.255.255.1\r"
expect -exact "Password: "
send -- "C4n4r1Wh4rf!!!\r"
expect -exact "887VA#"
send -- "show interface vlan 2\r"
expect -exact "Vlan2 is up, line protocol is up \r"
send -- "logout\r"
expect eof
When I run it, seems that it doesn't terminate itself correctly and I don't have back my bash shell. Only If I press ctrl+c i get my terminal
[...]
0 output buffer failures, 0 output buffers swapped out
887VA#logout
Connection to 10.255.255.1 closed by remote host.
Connection to 10.255.255.1 closed.
root#blackbox:/etc/myscripts#
I don t have
my bahs
shell
^Croot#blackbox:/etc/myscripts#
-bash: I: command not found
root#blackbox:/etc/myscripts#
This is a problem for me because I need to run that script within a bash script and save the output on txt file and then edit it

Expect script failing to continue

I have an expect script which is logging onto a device and sending a command, however the script completes without finishing, its like the last expect isn't being picked up.
How do I enable debugging to watch the script progress so I can identify where the issues is?
My script looks like this...
#!/usr/bin/expect
set hostname [lindex $argv 0]
set username "a.user"
set password "a.pass"
spawn telnet $hostname
expect "Username:" {
send "$username\r"
expect "Password:"
send "$password\r"
}
expect "#" {
send "sh ver\r"
}
It stops processing at the below line;
expect "#" {
however I can clearly see the # in the last output.
Thanks

How to use error codes inside bash script

Apologies if this has been asked before, I was unable to find an answer...
I have created a bash script for OS X to mount an AFP share. The script works successfully and allows a user to type their username and password into a graphical popup using cocoa dialog
My issue is that if the username and password are entered incorrectly, I want to be able to display a new pop up window explaining this.
I was trying to do this based on the exit status of the script but the script returns 0 regardless of whether or not the mount was successful
The script is as follows;
cd=/etc/cocoaDialog.app/Contents/MacOS/cocoaDialog
rvUsername=($($cd standard-inputbox --title "Network Mount" --informative-text "Please enter your username"))
username=${rvUsername[1]}
rvPassword=($($cd secure-standard-inputbox --title "Network Mount" --informative-text "Please enter your password"))
password=${rvPassword[1]}
mkdir "/Volumes/Test"
expect <<- DONE
set timeout -1
spawn /sbin/mount_afp -i "afp://servername/Software" /Volumes/Test
expect "*?ser:*"
send "$username\n"
expect "*?assword:*"
send "$password\r"
expect EOF
DONE
If I run this via Terminal or Textmate, I receive the following (as an example)
spawn /sbin/mount_afp -i afp://server/Software /Volumes/Test
User: username
Password:
mount_afp: the volume is already mounted
Program exited with code #0 after 7.32 seconds.
So mount_afp is giving me a message, which I would then like to use in my script...but the exit code is 0 and I don't know how else to get hold of that message to use
Any ideas? (Hope that makes sense!)
To get the exit code of the spawned command, use something like this:
# wait for the command to end : wait for the prompt $ followed by space
expect "\\$ "
send "echo \$?\r"
expect -re "(\\d+)" {
set result $expect_out(1,string)
}
send_user "command exit with result code : $result"
This will take the content of the variable $? (which is the exit code of the prevously ended command) and save it to $result.
Thanks for the responses all, helped point me in the right direction
I ended up taking this approach and setting the expect command as a variable
output=$(su -l $loggedInUser -c expect <<- DONE
set timeout -1
spawn /sbin/mount_afp -i "afp://$serverName" /Volumes/mount
expect "*?ser:*"
send "$username\n"
expect "*?assword:*"
send "$password\r"
expect EOF
DONE)

How to use Bash script variables in Expect conditional statements

I am writing a Bash script and using Expect to do sftp. Now in the Expect block I want to access a Bash variable in a conditional statement. But, I am unable to do so. How can do this?
Also, the execution of this script is controlled from a C program and I want redirect the output to a log file (which again is dynamic). Can I do that and suppress all the output on standard output.
Here is the code:
!/usr/bin/bash
host=$1
user=$2
pass=$3
action=$4
path=$5
echo "Starting...."
function doAction {
strAction="\""$action"\""
echo $strAction
/usr/bin/expect <<EOF > logfile.txt
**set bashaction $strAction**
spawn sftp $user#$host
expect "password:"
send "$pass\r"
expect"sftp>"
send "cd $path\r"
**if {$bashaction == "TEST"} {**
expect "sftp>"
send "prompt\r"
}
expect "sftp>"
send <sftp command>
expect "sftp>"
send_user "quit\n"
exit
EOF
}
doAction
echo "DONE....."
For 1. using an Expect script instead worked.
For the logging issue, using log_user 0 and log_file -a <file> helped.
You don't need to use Bash. Expect can handle all that:
#!/usr/bin/expect
set host [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
set action [lindex $argv 3]
set path [lindex $argv 4]
puts "Starting...."
puts "\"$action\""
spawn sftp $user#$host
expect "password:"
send "$pass\r"
expect"sftp>"
send "cd $path\r"
if {$action == "TEST"} {
# Do something
} else {
# Do something else
}
expect "sftp>"
send_user "quit\r"
puts "DONE....."
Coming from Bash, the Tcl/Expect syntax is a little strange, but you should not have any problem expanding the above skeleton.
Accessing Environment Variables from TCL and Expect
Since you are calling this Expect script from another process, you can make use of environment variables. For example, if your parent process has exported action to the environment, then you can access its value within your expect script with:
$::env(action)
In Bash, you can mark the variable for export with the export builtin. For example:
export action
Since I'm not sure how you're invoking the Expect script from C, it's up to you to make sure the variable is properly exported.
Disable Logging to Standard Output
To disable logging to standard output from spawned processes, Expect provides the log_user command. You can prevent your spawned processes from writing to stdout with log_user 0.
The expect(1) manual says:
By default, the send/expect dialogue is logged to stdout (and a
logfile if open). The logging to stdout is disabled by the command
"log_user 0" and reenabled by "log_user 1". Logging to the logfile
is unchanged.
This doesn't actually close standard output, which is generally not what you want anyway. Doing so will cause anything that writes to stdout to throw an error like this:
can not find channel named "stdout"
while executing
"puts hello"
(file "/tmp/foo" line 8)
To suppress output to the standard output you can use
command here >/dev/null 2>/dev/null
To write to a log file, you can use similar piping (> or >>), or the tee command if you want to write the output in the middle of a long pipe.

Remote SSH and command execution using BASH and Expect

I'm trying to achieve the following with a bash script:
try for SSH connection, if fails, error out if SSH connection is
once confirmed, execute the 'top' command and save results to file
scp file back from remote server
I know the invidiual commands, which would be:
1) to check the ssh connection:
ssh -q user#ip exit
echo $?
This should return '0' on success and '255' on error.
2) to execute top and save to file would be:
top -n 1 -b > /tmp/top.out
3) scp back file from remote host
expect -c "
set timeout 1
spawn scp user#host:/tmp/top.out root#anotherhost:/.
expect yes/no { send yes\r ; exp_continue }
expect password: { send password\r }
expect 100%
sleep 1
exit
"
Now putting this altogether is my problem, to be more specific:
I can't seem to be able to get the returned '0' and '255' values when using expect to test the SCP connection.
I can't seem to be able to execute the top command using expect again, i.e. this doesn't work:
expect -c "
set timeout 1
spawn ssh user#host top -n 3 -b > /tmp/top.out
expect password: { send password\r }
sleep 1
exit
"
and therefore the 3rd bit won't work either.
Any help is appreciated. Please bear in mind that my script is a .sh script with the #!/bin/bash declaration -- I cannot use #!/usr/bin/expect for various reasons.
Now that's ugly stuff. There are many pitfalls, probably you are not quite sure at what places you execute with a shell, or with plain argv array. Also the expect stuff is not the way that is supposed to be done, that's only brittle.
(AFAIK, expect is mainly meant to be used to communicate with modems (AT command set) and the like).
Use SSH keys for automated ssh and scp instead of passwords. Once you've done that (or even before, but then you have to enter passwords manually), launch this in your shell:
$ ssh user#server "top -n 1 -b" > /tmp/top.out
and the file will be on your local machine. (Because redirection was done locally, not yet remotely). No need to scp.
That's all there is to it.
Check this one:
https://stackoverflow.com/a/23632210/524743
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.

Resources