expect with shell for multiple taksks of cloud foundry CLI - shell

I have a task of creating docker container and working through its lifecycle. To automate it I am using shell with expect as cloud foundry cli is interactive
below is my code:
#!/usr/bin/expect -f
spawn cf login -a <api url>
#set timeout 30
expect "Email>"
send "email id\r"
expect "Password>"
send "passw0rd"
expect -exact ">"
send "2\r"
Till above line, the code is working as expected.From the last 2 lines above it can be seen that, I have to enter 2 at '>'. After this the $ prompt like below is shown which ends with '$':
expect "trishal#trishal-VirtualBox:~/PycharmProjects/Trishal- VC/containers$"
send "cf ic init"
Now, after coming on '$' prompt, I expect the code to accept 'cf ic init' which is a command to initialize the work space. Its not accepting, the output stays at '$' prompt itself and doesn't proceed. I had also tried below:
expect -exact "$"
send "cf ic init"
no luck
screen remains at
trishal#trishal-VirtualBox:~/PycharmProjects/Trishal-VC/containers$

expect -exact "$" will work for sure to match the literal dollar. All send commands should be sent with \r which represents the 'Enter' key. After the last command, add expect eof if your program will complete at that time and no further interaction required.

Related

Expect script to wait for Heroku ssh prompt

I want to use an expect script to run an SSH command on Heroku dynos (for example: Get IP of Heroku dynos)
The exact output when logging into the dyno is:
▸ heroku-cli: update available from 6.12.9 to 6.14.21-d83c94b
Establishing credentials... done
Connecting to <dyno> on ⬢ <app>...
~ $
I'm sure the first line would be gone after updating. It takes several seconds for this authentication/login to actually occur.
A simple expect script could go like this:
#!/usr/bin/expect
spawn "./ssh_into_heroku"
expect "xxxx"
send "my_command"
I've tried expect "~ $" { send "my_cmd" } and expect "~ $ " { send "my_cmd" } but neither are working and I have no idea how to debug this.
What would go in the "expect" portion to make this work?
You're waiting for the prompt that ends with a space, a dollar sign and (I'm guessing) another space. You would use this:
set prompt_regex { \$ $}
expect -re $prompt_regex

expect telnet to multiple cisco devices and execute show run

I have the following scripts which is supposed to pull the IP address from a file device-list.txt and then telnet to the device, login and execute a show run. The script logs in to the device successfully, but gives me an error after that. Any ideas where im going wrong?
for device in `cat device-list.txt`; do ./test4 $device;done
cat test4
#!/usr/bin/expect -f
set hostname [lindex $argv 0]
set user myusername
set pass mypassword
set timeout 10
log_file -a ~/results.log
send_user "\n"
send_user ">>>>> Working on $hostname # [exec date] <<<<<\n"
send_user "\n"
spawn telnet $hostname
expect "Username:"
send "$user\n"
expect "Password:"
send "$pass\n"
expect "#"
send “term len 0\n”
send “show running-config\n”
expect “end\r”
send “\n”
send “exit\n”
User Access Verification
Username: myusername
Password:
ROUTER#usage: send [args] string
while executing
"send “term len 0\n”"
(file "./test4" line 26)
Your problem is due to incorrect double quotes. You have to use double quotes ", not smart quotes “
send “term len 0\n” ; # Wrong
send "term len 0\r"; # Correct
Note :
Basically, Expect will work with two feasible commands such as send and expect. If send is used, then it is mandatory to have expect (in most of the cases) afterwards. (while the vice-versa is not required to be mandatory)
This is because without that we will be missing out what is happening in the spawned process as Expect will assume that you simply need to send one string value and not expecting anything else from the session.
So, we recommend to use expect after each send, to make sure our commands actually sent to the spawned process. (You have not used expect after sending some commands such as term len 0\r). Also, use \r instead of \n.

Writing strings to standard input in bash script

I'm trying to automate a startup of a specific service with bash
When the service is started with init.d (/etc/init.d/openvpn.custom) it is promting for username and then password - and then it connects
The auth-user-pass from-file is not possible with the installed version, and it cannot be upgraded because of dependencies
So i'm trying to write a simple bash scripts that executes the init.d script, sleeps for a bit, inputs the username, returns, sleeping a bit, inputting the password - you'll get the flow.
like http://pastebin.com/qWHX7Di5
I've experimented with echo, but it doesent seem to work
This is for a rather legacy firewall i'm asked to keep connected.
Is this even possible?
I would use expect instead of bash. You can still call it from within bash if you need to do other tasks as well.
In expect, the script would be something like the following (untested):
#!/usr/bin/expect -f
set username "username"
set password "password"
spawn /etc/init.d/openvpn.custom start
expect "Username:"
send "$username\r"
expect "Password:"
send "$password\r"
expect eof
You'd want to change the expect "Username:" & expect "Password:" lines to match the actual login prompts that are output by your init.d script.
See the expect man page for further details.
You can try using a here-doc:
/path/to/init.d << END
$username
$password
END

Problems with terminating connection after running scripts on remote computer using shell script

This is the first time I am writing a shell script. I tried to do as much research as I can to avoid dumb/repetitive question. Please excuse if its repeat/dumb question.
I have a shell script which connects to remote linux machine and runs scripts there. I am using 'expect' to spawn a ssh connection and to issue commands to trigger the job. However, I am having issues while closing the connection after completing the job.
This is my script:
set prompt "(%|#|\\$|%\]) $"
expect -c 'spawn ssh $UN#$STAGE ;
expect password ; send "$PASS \n";
expect -regexp "$PROMPT"; send "./settings.$UN.sh > settings_log.txt \n";
interact'
This script successfully runs the script file for me ($UN and $STAGE parameters are input to the script. I omitted that here for simplicity). However, this leaves me with an open connection.
I tried to close the connection after running the script by using following instead of above
expect -c 'spawn ssh $UN#$STAGE ;
expect password ; send "$PASS \n";
expect -regexp "$PROMPT"; send "./settings.$UN.sh > settings_log.txt \n";
expect -regexp "$PROMPT"; send "exit \n"'
This does close the connection but I noticed that my script file did not run at all. Also the settings_log.txt is not generated at all.
Does this mean, that exit command is aborting the process before its completion? I tried using 'sleep' before exit but it did not help. Is there a better suggested way to terminate the connection when using expect?
Any help is appreciated.
with expect, you terminate your send commands with \r not \n, so
expect -c 'spawn ssh $UN#$STAGE
expect password
send "$PASS\r"
expect -regexp "$PROMPT"
send "./settings.$UN.sh > settings_log.txt\r"
expect -regexp "$PROMPT"
send "exit\r"
expect eof'
Note you can execute remote shell commands and copy files using ssh and scp, directly, without using expect.
For example,
scp ./settings.$UN.sh $UN#$STAGE:settings_log.txt
ssh $UN#$STAGE whatever-you-need-to-execute
The connection will close as soon as soon as whatever-you-need-to-execute completes.
Your outer script seems to be written in csh and sets a variable named "prompt", but your expect script is using a variable called "PROMPT". Try making the two variable names match case.

Conditional statament inside expect command called from a bash script

I've been trying to automate some configuration backups on my cisco devices, i've already managed to do the script that accomplishes the task but I'm trying to improve it to handle errors too.
I think that's necessary to catch the errors on two steps, first just after the 'send \"$pass\r\"' to get login errors (access denied messages) and at the 'expect \": end\"' line, to be sure that the commands issued were able to pull the configuration from the device.
I've seen some ways to do it if you work on a expect script, but i want to use a bash script to be able to supply a list of devices from a .txt file.
#!/bin/bash
data=$(date +%d-%m-%Y)
dataOntem=$(date +%d-%m-%Y -d "-1 day")
hora=$(date +%d-%m-%Y-%H:%M:%S)
log=/firewall/log/bkpCisco.$data.log
user=MYUSER
pass=MYPASS
for firewall in `cat /firewall/script/firewall.cisco`
do
VAR=$(expect -c "
spawn ssh $user#$firewall
expect \"assword:\"
send \"$pass\r\"
expect \">\"
send \"ena\r\"
expect \"assword:\"
send \"$pass\r\"
expect \"#\"
send \"conf t\r\"
expect \"conf\"
send \"no pager\r\"
send \"sh run\r\"
log_file -noappend /firewall/backup/$firewall.$data.cfg.tmp
expect \": end\"
log_file
send \"pager 24\r\"
send \"exit\r\"
send \"exit\r\"
")
echo "$VAR"
done
You need alternative patterns in the expect statements where you want to catch errors. If you're looking for a specific error message you can specify that, alternatively just specify a timeout handler which will eventually trigger when the normal output fails to appear.
Eg. after send \"$pass\r\" instead of expect \">\" try:
expect \">\" {} timeout {puts stderr {Could not log in}; exit}
ie. if the expected output arrives before the timeout (default 10 sec) do nothing and continue, otherwise complain and exit from expect. You might also need an eof pattern to match the case where your ssh session ends.
Note that since you don't do any variable substitution in expect, you don't need \"\" around your strings, you can use {} or even nothing when it's one word, eg. expect conf and send {no pager}.
BTW I agree with bstpierre that this would be cleaner if you dropped bash and did the whole thing in expect, but if bash does the job that's ok.
If you don't use single quotes (expect -c '...'), then all the $variables will be substituted by bash not expect. May be easier to put the expect code in a separate file, or maybe a heredoc.

Resources