Expect script not working and send status getting skipped - expect

I have written a script to execute a setup on the server, but while executing send statement is getting skipped and other one getting executed.
Below is my script :
enter code here[code written][1]
====================ERROR m getting=============================
Enter the name of the new namespace you want to create [coe_ns] :
Enter the name of the clearinghouse you want to create [coe_ch] : invalid command name "C"
while executing
"C"
invoked from within
"expect -re "* Option to execute (Continue/Restart/Quit) [C]: $""
(file "./TNS_Server_Setup.sh" line 21)
I have a doubt that send command before that is not getting executed, also am I passing "ENTER" right way.

expect uses square brackets as the command substitution syntax (in the same way that the shell uses backticks or $(...). So when you write
expect -re "* Option to execute (Continue/Restart/Quit) [C]: $"
expect sees [C] and tries to invoke the command C.
Also, that standalone * will throw a "quantifier operand invalid" error, because that's a special character for regular expressions. You either need to escape it or remove it from the expression.
Use a different quoting mechanism that inhibits command substitution (like in the shell where you'd use single quotes)
expect -re {Option to execute (Continue/Restart/Quit) [C]: $}
You did not include your code in the question so we can't see your send command, but to "just hit enter", you would send "\r"
Update:
If you need to match multiple things, the expect command can do that: give it a list of pattern action pairs. Expect will match any of them.
expect {
-re {Option to execute (Continue/Restart/Quit) [C]: $} {
send "\r"
exp_continue
}
-re {enter the datatype $} {
send "array\r"
exp_continue
}
"some pattern that denotes we're done"
}

Related

Can't run an expect script: invalid command name "Yes/No"

I have this expect script that need to execute some other shell script to accept a Licence agreement
#!/usr/bin/expect
spawn ./xxx.sh
expect -ex "--More--"
send -- " "
expect "Do you agree with this license ?[Yes/No]"
send "Y\r"
But when I run it I get this error
invalid command name "Yes/No"
while executing
"Yes/No"
invoked from within
"expect "Do you agree with this license ?[Yes/No]""
(file "./xxx.sh" line 5)
I don't know what I'm doing wrong
expect is an extension of the Tcl language. In Tcl, you use square brackets for command substitution. Like the bash shell, command substitution occurs within double quoted strings.
To prevent your code from attempting to execute the Yes/No command:
use different quotes: Tcl uses curly braces as the non-interpolating quotes:
expect {Do you agree with this license ?[Yes/No]}
escape the brackets to prevent command substitution:
expect "Do you agree with this license ?\[Yes/No\]"

capture expect ssh output to variable

Hey am new to bash scripts and was wondering how would I capture the output of the ssh command into a bash variable? I looked around and cant seem to get it right. I have tried puts $expect_out(buffer) but when echo it says variable does not exist
I know the response should be just one line and if I want to save that into a variable response and then echo it how would I do that?
A generic idea can be something like as below.
spawn the ssh session
make proper login
Send each commands with send
Wait for desired output with expect
Example:
spawn ssh $user#$domain
expect "password" { send "$pwd\r"}
expect "#"; # This '#' is nothing but the terminal prompt
send "$cmd\r"
expect "#"
puts $expect_out(buffer); #Will print the output of the 'cmd' output now.
The word to wait for after executing the command can vary based on your system. It can be # or $ or > or :; So, make sure you are giving the correct one. Or, you can provide a generalized pattern for the prompt as such
set prompt "#|>|:|\\\$"; # We escaped the `$` symbol with backslash to match literal '$'
While using the expect after sending the commands, it can be used as
expect -re $prompt; #Using regex to match the pattern`

Expect: wrong # args: should be "file option ?arg ...?"

I'm using a code made my another person (we'll call it Code.x) that has a very cumbersome interactive command line interface. I often perform repetitive processes using this code and ideally I'd like to automate these tasks.
Code A asks for me to give a name for a file I want to output and then if that file name exists, it will ask me if I'd like to overwrite.
In that vein, I've written an expect script that navigates through the user prompts in Code.x:
#!/usr/bin/expect
spawn Code.x
set timeout 5
...[code]...
expect "file name" {send "foo.bar\r"} #Name of file
expect "Overwrite?" {send "y\r"} #Yes, overwrite the file
expect "Next Prompt" {send "next response\r"}
...[code]...
If the file already exists and needs to be rewritten, the rewrite prompt of course appears and everything works. However, if the rewrite prompt does not appear and instead Code.x gives the Next Prompt, I get the following error immediately after the prompt appears:
wrong # args: should be "file option ?arg ...?"
while executing
"file"
invoked from within
"expect "Overwrite?" {send "y\r"} #Yes, overwrite the file"
If I remove the curly braces around the send statement, the code works fine whether a rewrite is required or not. My impression from the information I've read is that the curly braces should mean that the send statement is only executed if the expect statement is satisfied. If that statement is not satisfied, I would assume that the expect code would wait until the timeout had been reached and then move on. What is actually happening here?
On another note, is is actually worth it to use expect to accomplish this task? I plan to embed this in a loop and create a number of different files.
expect "file name" {send "foo.bar\r"} #Name of file
expect "Overwrite?" {send "y\r"} #Yes, overwrite the file
Tcl (and hence expect) comments are a bit weird compared to other languages. A # is only a comment if it appears as the first character of a word that shows up where a command would appear: see http://tcl.tk/man/tcl8.5/TclCmd/Tcl.htm , rule #10
So you want an explicit semicolon to terminate the command, and then a comment can follow
expect "file name" {send "foo.bar\r"} ; #Name of file
expect "Overwrite?" {send "y\r"} ; #Yes, overwrite the file
Here's why the error occurs: the expect command takes a list of {pattern} {action} pairs (or a single list containing "pattern action" pairs). The last pattern does not require an action. So with your misguided comments, expect saw the equivalent of this:
expect {
"file name" {send "foo.bar\r"}
"#Name" {of}
"file"
}
expect {
"Overwrite?" {send "y\r"}
"#Yes," {overwrite}
"the" {file}
}
There must have been a "the" that expect saw before "Overwrite?", so it dutifully tried to execute the action block: {file}. The Tcl file command requires a subcommand, did not get one, and expect threw the error.

Expect exits too soon

I have the following bash script (script.sh):
#!/bin/bash
read -p "Remove? (y|n): " answer
echo "You answered '$answer'."
and I would like to drive it using expect. I have the following script (expect.exp, in the same directory):
#!/usr/bin/expect -f
set timeout -1
spawn -noecho ./script.sh
expect "^Remove"
send "y\r"
but it doesn't work as expected (pun intended). The result is:
~/Playground$ ./expect.exp
Remove? (y|n): ~/Playground$
So, the expect script somehow fails on the first 'expect "^Remove"' line and exits immediately, and the rest of script.sh does not execute. What am I doing wrong here?
I have been following the basic tutorials found online (the ones with the ftp examples). I am using expect 5.45 on Kubuntu 12.10.
Edit
So it changes if I add either 'interact' or 'expect eof' at the very end. But I have no idea what happens and why. Any help?
Two things I see:
"^Remove" is a regular expression, but by default expect uses glob patterns. Try
expect -re "^Remove"
while developing your program, add exp_internal 1 to the top of the script. Then expect will show you what's happening.
Ah, I see that expect adds special meaning to ^ beyond Tcl's glob patterns.
However, because expect is not line oriented, these characters (^ and $) match the beginning and end of the data (as opposed to lines) currently in the expect matching buffer
So what you see is that you send y\r and then you expect script exits as it has nothing more to do. When your script exits, the spawned child process will be killed. Hence the need to wait for the spawned child to end first: expect eof
Problem
You are not matching any text after the shell script's prompt, so the buffer for your spawned process never gets printed. The script finishes, the spawned process closes, and that's the end of the story.
Solution
Make sure you expect a specific response or your shell script's EOF, and then print your buffer. For example:
#!/usr/bin/expect -f
spawn -noecho "./script.sh"
expect "Remove" { send "y\r" }
expect EOF { send_user $expect_out(buffer) }

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