Need to spawn a sourced shell script using expect - bash

i'm using expect to automate some bash scripts. All goes right until i need to execute a sourced script; then i get this error:
couldn't execute "source setNAME.sh": no such file or directory
while executing
"spawn "source setNAME.sh""
(file "./setNAME.exp" line 2)
I use expect in a basic way (spawn/expect/send/interact); the problem is with spawn. Let me show a very easy example:
Script to read a name, echo the value and export to the environment as 'NAME':
/home/edu/scripts [] cat setNAME.sh
#!/bin/bash
echo "Your name?"
read name
export NAME=$name
echo "Provided NAME is $name"
If you execute with 'source' then the input value will be exported as environment variable. Just what i need:
/home/edu/scripts [] source setNAME.sh
Your name?
Dennis
Provided NAME is Dennis
/home/edu/scripts [] echo $NAME
Dennis
Let's to use expect to avoid interaction, setting 'edu' as the name:
/home/edu/scripts [] cat setNAME.exp
#!/usr/bin/expect
spawn setNAME.sh
expect "Your name?"
send edu\r
interact
/home/edu/scripts [] setNAME.exp
spawn setNAME.sh
Your name?
edu
Provided NAME is edu
/home/edu/scripts [] echo $NAME
Dennis
But the name, obviously, is still Dennis: 'edu' is shown at sh script output but not exported as NAME because expect script spawns the setNAME.sh script WITHOUT source (or dot before script).
The problem is that I CAN'T DO THIS because i get the error commented at the beginning.
Any ideas to solve/afford this need ?
In my case the script is much more complex and other solutions are also invalid, for example things like this:
source script.sh << EOF
<option1>
<option2>
...
EOF
That's the reason i'm trying to use expect...
Thanks a lot!

source is a shell builtin, not a system command, so you first need to spawn a shell then send the source command to it. That shell will then have the value in the NAME variable

Related

Passing variable to Expect and Spawn

I'm writing a script that will scp a tar file from my local server to a remote host. Since the script generates the file through a pre-requisite process, the name is generated dynamically. My script needs to take the name of the file and pass it to scp for transfer.
#!/usr/bin/expect -f
spawn scp test.$(date +%y%m%d_%H%M).tar user#IP-ADDRESS:/destination/folder
set pass "password"
expect "password: "
send -- "$pass\r"
expect eof
I've tried setting the filename as a variable but keep seeing the same error:
can't read "(date +%y%m%d_%H%M)": no such variable
while executing "spawn scp test.$(date +%y%m%d_%H%M).tar user#IP-ADDRESS:/destination/folder"
$(date +%y%m%d_%H%M) is not a Tcl command. If you use expect, you have to learn Tcl. To get a formatted date in Tcl, use the clock command. Also, interpolation of the result from a command in Tcl is not done by $(....), but by [....]. You can find examples for this construct here.
Decided to go another route since the team was able to provision a new Artifactory repo for this binary and alike. However, to the advice provided here I was able to make a few discoveries which I used to fix my issues:
I also had a password with $ symbol and that also caused a world of issues.
#!/bin/bash
TEST=$(date +%y%m%d_%H%M)
/usr/bin/expect <<eof
set password {pas\$word}
spawn scp "$TEST" user#IP-ADDRESS:/destination/folder
expect "*password:"
send "$pasword\r"
expect eof

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.

Problems to get a remote shell variable

I have a problem with an execution of a shell script into a remote shell.
I can't get value of $ARQ_END.
ssh -T user#MACHINE << 'EOSSH'
/app/work/leo/ReturnFileName.sh #This script returns a filename like: ADDRESS_BR_RECIFE_20170913.txt
ARQ_END="`/app/work/leo/ReturnFileName.sh`"
EOSSH
echo $ARQ_END #Returns nothing! Expected to return: ADDRESS_BR_RECIFE_20170913.txt
Setting a variable in a subshell isn't visible in the parent shell. You need to set the variable directly in the parent shell. The way to do that is to pass the output of ReturnFileName.sh up through the ssh session and to the parent shell and capture it there.
ARQ_END=$(ssh user#MACHINE /app/work/leo/ReturnFileName.sh)
echo "$ARQ_END"
Thanks, it works!
I used the case as you posted:
ARQ_END=$(ssh user#MACHINE /app/work/leo/ReturnFileName.sh)
echo "$ARQ_END"

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

How to return a value from child expect script to parent sh script

I have a expect script inside a shell script. My problem is I am unable to get a variable value from the child expect script to the shell parent script.
Please find my code below:
#!/bin/sh
expect <<- DONE
spawn telnet myemailserver.com imap
expect "* OK The Microsoft Exchange IMAP4 service is ready."
send "a1 LOGIN myuser mypass\r"
expect "a1 OK LOGIN completed."
send "a2 EXAMINE INBOX\r"
expect "a2 OK EXAMINE completed."
send "a3 SEARCH UNSEEN\r"
expect "a3 OK SEARCH completed."
set results $expect_out(buffer)
set list [split $results "\n"]
send "a4 LOGOUT\r"
expect "Connection closed by foreign host."
spawn echo $list
expect eof
DONE
echo $list
exit 0
I found out that the variable list at the last line is empty. Is there a way to pass the value from variable $list to the shell parent script?
Your here-document is subject to shell variable expansion before the script is given to the expect interpreter. The $list variable is substituted with nothing (assuming you don't already have a shell variable named list in your program). You need to ensure the here-doc is single quoted (shown below)
Just like working with awk or sed, the shell inter-process communication is performed through passing data along the standard IO channels: the shell script has to capture the output of the expect program:
list=$( expect <<'END'
log_user 0
# expect program here
puts $list
END
)
echo $list
Since I'm suppressing normal terminal output of spawned programs with log_user 0 in order to send only the crucial information back to the shell, you have to replace spawn echo with expect's puts command.

Resources