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

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.

Related

Expect script not working and send status getting skipped

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

Expect Script - varying sends

I'm working on expect script to call a installer.sh
For a neat installations , it works fine.
But when the installer fails for pre-checks, the order of the send differs and i have no control on the order.
spawn ./Installer.sh
expect "change? (Y/N)"
send "Y\r"
expect "path"
send "$path\r"
expect "Enter selection"
send "1\r"
expect "path"
send "$path
exit 0
After 2nd expect "path", the installer validates internally and proceeds with step3 and continues and finishes.
But if after second expect path , installer pre-checks fails then it exists and prompts for last step ie for the path again.
currently when the script displays after exiting and prompts for 4th, it continues to send the 3rd response which is irrelevant.Does the script does not validate fr the match expect string?
Error :
"No Space. Exiting.
**Path: 1**
cp: cannot create regular file `1': Permission denied
send: spawn id exp5 not open
while executing
"send "path\r""
The shell script exits for various reasons and prompts for last send.
Is there a way to get the last display message expect_out while the session is ongoing and read it and continue based on it.
spawn shellscript
expect_1
send_1
expect_2
send_2
--sh stops and displays exiting...
if expect_out(buffer)=exiting
then
expect_4
send_4
else
expect_3
send_3
expect_4
send_4
exit
The key thing you probably need is the ability to expect several different things at once. Fortunately, this is quite easy to do.
expect {
"change? (Y/N)" {
# Something in here to respond to this case
# This bit is just code, but could be effectively empty too
}
"Exiting" {
# Now we've detected that the installer failed
send_user "oh no!\n"
exit 1
}
}
When using this form, you can restart the current expect from within a handler script by finishing it with exp_continue. As usual, you've got to think careful about what actual patterns to match.

Set OS X password using expect

I'm trying to write a script to update the password of an OS X account to a rotating, centrally-stored value. As a prelude to learning to use tclcurl, I just want to get this prototype script working:
#!/usr/bin/expect
set mgrpassword "newpassword" # this will become a tclcurl command later
spawn passwd manager
expect "New password:"
send "$mgrpassword\n"
expect "Retype new password:"
send "$mgrpassword\n"
puts "\nManager password changed."
exit 0
It runs without errors, but it does nothing; the password for the manager account remains unchanged. I've tried it with both \r and \n but that didn't make any difference. Can anyone see what I'm doing wrong or what steps I'm omitting?
(It will always run with admin rights; that's why there is no 'expect "Old password:"' line.)
Just add one more expect statement at the end, like as follows,
send "$mgrpassword\r"
expect eof
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.
Your script can be written in the following way as well which makes it robust with the use of exp_continue. It will make the Expect to run again.
set mgrpassword "newpassword"
spawn passwd manager
expect {
timeout { puts "Timeout happened";exit 0}
"password:" {send "$mgrpassword \r";exp_continue}
eof {puts "Manager password changed"; exit 1}
}
So it turns out that
dscl . passwd /Users/manager [passwordstring]
works a lot better/easier than trying to combine passwd with expect. Hope this helps someone else.

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

How to make expect command in expect program script to wait for exact string matching

I want to know how to make expect command in expect script to wait for exact string to be matched before proceeding to next line in the script.
Bourne shell (sh) script:
#!/bin/sh
#!/usr/bin/expect
spawn ssh -Y localuser#lbblr-tirumala
expect -exact "localuser#lbblr-tirumala's password: "
# replace password with ur own passwd
send "geomax45\r"
# replace the prompt with ur own prompt
expect "localuser#lbblr-tirumala:~$ "
# run application
send "./sample\r"
expect "*Menu*\r
1. print hello world\r
2. print Bye world\r
3. quit\r
enter choice: "
# enter choice 1
send "1\r"
expect "Bye world\r
*Menu*\r
1. print hello world\r
2. print Bye world\r
3. quit\r
enter choice: $"
# enter choice 2
send "2\r"
In the above code after entering 1 the code should wait for "Bye world\r......" string to occur on the screen. But even though the screen prompts different string altogether, the code is executing the next line and entering option 2, which should not occur as per definition of expect. It should wait until the complete string in expect command matches.
I want to know whether there is any stricter command that can be used so that expect waits until exact string matching occurs. Please help me in this issue
You're already using it earlier in your script: expect -exact. But long matching strings are pretty much guaranteed to cause problems; you should try to cut it down to the shortest unique match, if necessary breaking it into multiple expect commands.
In this case, just based on what you show, the proper command is probably expect -exact "choice:". But inherently it is not possible to determine this without full details of the possible program outputs.

Resources