bash: Interactive scripting with expect_out not working - bash

I am trying to run a script interactively with expect & send in bash.
The main script prints following to console
The available files are...
1 File_001.bin
2 File_002.bin
3 File_003.bin
and I want to find index of a specific file say File_002.bin, i am writing interactive script like below
#!/usr/bin/expect -f
spawn mainScript.sh;
expect -re "^.*File_002.bin.*$";
set filename $expect_out(0,string);
send_user "The filename is: $filename\n";
But its not matching anything! Any idea what's the problem?

Related

Write ActiveState Tcl Program for Windows that expects a password and enters it

I want to write a shell program in windows that runs another shell script and expects a password prompt from the Git bash terminal and inputs it.
This is what I have so far:
#!/bin/sh
# \
exec tclsh "$0" ${1+"$#"}
package require Expect
spawn sampleScript.sh
expect "Password:"
send "pass123"
sampleScript.sh code:
echo 'Hello, world.' >foo.txt
my program outputs the following:
'The operation completed successfully. while executing "spawn sampleScript.sh"
(file "compare.tcl" line 6)'
However, there is no foo.txt that is created in my local file folder where the scripts are. Can you help?
The key with expect programs is to let the spawned program exit gracefully. As it currently stands, after your expect script sends the password, it immediately exits, and that kills the spawned program too early.
If you don't need to interact with the sampleScript (i.e. just let it run to completion), the last line in the expect script should be
expect eof
Otherwise, use
interact
Read How to create a Minimal, Reproducible Example -- your updated code does not reproduce the error you're seeing
Tcl code:
when you send something, you usually need to "hit Enter": send "password\r"
Did you add expect eof to the Tcl script? If not, you might be killing sampleScript.sh before it has a chance to create the output file
sampleScript.sh: Is that really your sample script? Where's the password prompt?

How to get the command output in variable?

I executed the below code as .sh file and getting the result as expected. Now I need to store the output of the "send" command and do things further.
I have done the below bash code and now I am keeping the output in a file:
#!/usr/bin/expect -f
spawn iroot
expect ".* password for"
sleep 3
send "password\r"
sleep 5
send "dmidecode -t system | grep Manufacturer > /tmp/manfacdetails.txt\r"
send "exit\r"
interact
How can I do that?
It seems that you are using iroot to obtain root access to a phone and issuing a command as root.
I am assuming here that the command you already have is noninteractive, and produces the output you want if you take out the redirection to a file, without any human interaction.
Then, the simple matter of capturing its output is covered by the common FAQ How to set a variable to the output of a command in Bash?
#!/bin/bash
manufacturer=$(expect <<\____HERE)
spawn iroot
expect ".* password for"
sleep 3
send "password\r"
sleep 5
send "dmidecode -t system | grep Manufacturer\r"
send "exit\r"
interact
____HERE
if [ "$manufacturer" = "Motor Ola" ]; then
ola=1
fi
# Maybe you'll prefer case over if, though
case $manufacturer in
"Samsung" | "LG" ) korean=1 ;;
"Apple")
echo "$0: You're kidding, right?" >&2
exit 127;;
*) echo "$0: Unknown manufacturer $manufacturer" >&2
exit 1;;
esac
If this doesn't work for you then Use expect in bash script to provide password to SSH command has some variants you might want to try.
You also seem to be confused about the nature of scripts. Any executable file which has a shebang as its first line will be executable by whatever interpreter is specified there. Mine has /bin/bash so this is a shell script and more specifically a Bash script, while yours has expect as its interpreter, so it's an Expect script. You also commonly have Awk scripts and Perl scripts and Python scripts (and less commonly but not at all uncommonly scripts in many, many other languages).
As already mentioned, Expect is also a scripting language, and it is possible that you would like for yours to remain an Expect script, rather than a shell script with an embedded Expect script snippet. Perhaps then see expect: store output of a spawn command into variable
The name of the file which contains the script can be anything, but the standard recommendation is to not give it an extension -- Unix doesn't care, and human readers will be confounded if your .sh file is an Expect script (as it currently is).
Perhaps tangentially see also Difference between sh and bash as well as http://shellcheck.net/ which you can use to diagnose syntax errrors in your (shell) scripts.

Need modification in an ssh script that's written in expect

Am working on a script to ssh into list of servers using expect tool. Getting below error while running it
./script
#!/usr/local/bin/expect -f
while /usr/bin/read hostname
do
spawn ssh user#$hostname
expect "user#$hostname's password"
send "resuidt\n"
expect "user#$hostname"
interact
done < srvlist
Below is my error:
missing operand at _#_
in expression "_#_/usr/bin/read"
(parsing expression "/usr/bin/read")
invoked from within
"while /usr/bin/read hostname"
(file "./script" line 3)
Need help to fix this error.
You are writing an Expect program, which is basically a Tcl program. Your while loop is not Tcl syntax, but looks like a (Posix/Ksh/Bash/Zsh)-shell script.
You have to make up your mind: Write everything in Tcl, or split your application into two files: One (in shell script) as "main program", and a separate expect script, which will be called by the shell script.
As user1934428 indicates you are using bash-type while loop syntax.
Below is one example of how to make an expect script perform the actions you want.
#!/usr/local/bin/expect -f
set file hostname
set user myusername
set passwd mypassword
set f [open $file]
foreach target [split [read $f] "\n"] {
spawn ssh $user#$target
expect {
timeout {send_user "Expect Timeout\n" ; exit}
"password:"
}
send "$passwd\r"
expect {
timeout {send_user "Expect Timeout\n" ; exit}
"$user#$target"
}
interact
}
close $f
I included timeouts in the expect sections because I've found if you do not add these safety mechanisms the expect script can proceed even without the proper responses.
if you want to use shell variables directly into the expect script then you have to pass those variables as $env(shell_variable_name) inside the expect script
example:spawn ssh $env(myusername)#$env(hostname)

Writing shell script using another shell script

I'm trying to automate running of a shell script that would take some user inputs at various points of its execution.
The basic logic that I've in my mind is copied below, but this is only for one input. I wanna run it recursively until the shell prompt is received after the original script completes its execution. I said recursively because, the question that prompts for an input and the input itself will be the same all the time.
#!/usr/bin/expect
spawn new.sh $1
expect "Please enter input:"
send "my_input"
Sharing any short-cut/simple method to achieve this will be highly appreciated.
You don't need expect to do this - read can read from a pipe as well as from user input, so you can pass the input through a pipe to your script. Example script:
#!/bin/bash
read -p "Please enter input: " input
echo "Input: $input"
Running the script prompts for input as normal, but if you pipe to it:
$ echo "Hello" | sh my_script.sh
Input: Hello
You said that your input is always the same - if so, then you can use yes (which just prints a given string over and over) to pass your script the input repeatedly:
yes "My input" | sh my_script.sh
This would run my_script.sh, any read commands within the script will read "My input".

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

Resources