expect in bash script × 16172 - bash

I try to set a expect script in bash.
#!/bin/bash
/usr/bin/expect <<- EOD
set router 192.168.0.251
set user admin
set pass test
set timeout 1000
set filesave [exec date +%m-%d-%Y]
spawn telnet $router
send "\n"
expect "Username:"
send "$user\n"
expect "Password:"
send "$pass\n"
expect ">"
send "en\n"
expect "Password:"
send "$pass\n"
send "term len 0\n"
log_file $router--$filesave.cfg
send "show running-config\n"
expect "end\r"
send "\n"
log_file
send "exit\n"
EOD
cat /Users/test/Desktop/python/$router--$filesave.cfg | grep end
exit 0
I just got this output
./script2
spawn telnet
telnet> telnet>

Try changing your shebang from:
#!/bin/bash
to
#!/bin/expect
and remove:
/usr/bin/expect <<- EOD
And see if that works.
Update: if you need to have expect run as part of your bash script, either encapsulate the expect code in a separate script with a expect shebang and source it from your bash script, or encode it as in the following example:
expect_sh=$(expect -c "
spawn ssh $login#$IP
expect \"password:\"
send \"$password\r\"
expect \"#\"
send \"cd $dest_dir\r\"
expect \"#\"
send \"chmod +x $server_side_script $other_script\r\"
expect \"#\"
send \"./$device_side_script\r\"
expect \"#\"
send \"cat $deploy_count\r\"
expect \"#\"
send \"exit\r\"
")
echo "$expect_sh"

The problem is that bash is interpreting all the $variables before expect sees the script. Thus you're simply spawning telnet with no hostname given. Change:
/usr/bin/expect <<- EOD
to:
/usr/bin/expect <<- 'EOD'
This has the effect of single-quoting the entire here-document.
http://www.gnu.org/software/bash/manual/bashref.html#Here-Documents
Note also that the next bash command (cat /Users/...) relies on variables defined in Expect -- they are not defined in bash. Try this
#!/bin/bash
export router=192.168.0.251
export filesave=$(date +%m-%d-%Y)
/usr/bin/expect <<- 'EOD'
set router $env(router)
set filesave $env(filesave)
# the rest stays the same

Related

Running multiple expect/TCL code inside same shell script

I have to run multiple chunks of expect script inside the shell script. In between each expect scripts, I have to run shell code. Below is conceptual non-working code where, 1st chunk would SSH into a remote node, and the subsequent chunks will run some other commands on the same remote server.
#!/bin/bash
#first chunk of expect code
/usr/bin/expect - <<EOF
global spawn_id
spawn /bin/bash
expect -re ".*\$"
spawn -- "ssh -oStrictHostKeyChecking=no -oCheckHostIP=no admin#localstack1\r"
expect -re ".*assword.*"
send 'admin\r'
expect -re ".*\$"
interact
EOF
#running shell commands
ls -lrt
#2nd chunk of expect code
/usr/bin/expect - <<EOF
expect -re ".*\$"
send -- "hostname\r"
expect -re ".*\$"
EOF
This seems to be impossible, However would like to know more from experts. unlike pexepct, here I am unable to find any way to reuse the spawned shell(passing the child object).
why?
from another program I am getting the list of commands,expected_results and timeout , so I cannot hardcode them within the expect script. Sometime there are 1 set of commands,sometimes multiple set of commands.
Here's an example of writing the whole thing in Expect/Tcl as suggested in the comments:
#!/usr/bin/expect
#spawn /bin/bash
#expect -re ".*\$"
spawn -- "ssh -oStrictHostKeyChecking=no -oCheckHostIP=no admin#localstack1\r"
expect -re ".*assword.*"
send 'admin\r'
expect -re ".*\$"
interact
#running shell commands
puts [exec ls -lrt]
expect -re ".*\$"
send -- "hostname\r"
expect -re ".*\$"
I've commented out the spawn of bash because that appears redundant.

expect interact with <<EOF not work properly

i want a auto su script but
expect interact with <
this script work properly
#!/usr/bin/expect
set timeout 5
spawn su
expect "Password:"
send "123456\r"
interact
#expect "# "
#send "whoami"
but this not work
#!/bin/bash
su_user=$1
su_pwd=$2
/usr/bin/expect <<EOF
set time 30
spawn su $su_user
expect "assword"
send "$su_pwd\r"
send "whoami\r"
interact
EOF
after run the script the user do not change
why dose interact not work with <
I don't know for certain that this is your problem but I think interact is getting the EOF from stdin and aborting.
I'm having a similar issue trying to do
cmd | expect_script.
As my very simple testcase I'm trying to do 'read x; echo $x' on a remote server and "echo FRED" as my cmd but my script is aborting like this:
running read x; echo this is $x
send: sending "read x; echo this is $x\r" to { exp5 }
spawn id exp0 sent <FRED\n>
interact: received eof from spawn_id exp0
interact: spawn id exp0 not open
(You might find exp_internal 1 useful for debugging)

Why does my Expect script only echo the command not running?

I'm trying to automate some ssh process. I have my Expect code. But my Expect code only echos/prints out the command. It doesn't actually run the command.
#!/usr/bin/expect -f
set timeout 10
set usrnm "aaaaaa"
set pwd "pppppp"
set addr1 "xxx.cloud.xxx.com -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
set addr2 "xxx.xxxx.xxxx.com"
spawn ssh $usrnm#$addr1
expect {
"(yes/no)?" {send "yes\r";exp_continue}
"password: " {send "$pwd\r"}
}
expect "*#"
send "ssh $usrnm#$addr2\r"
expect {
"(yes/no)?" {send "yes\r";exp_continue}
"password:" {send "$pwd\r"}
}
expect "*#"
send "cd /tmp/myself/folder\r"
expect "*#"
send "./run_engine.sh test.py\r"
expect eof
#interact
So if I do
expect my_expect.exp
it just prints the command:
spawn ssh aaaaaa#xxx.cloud.xxx.com -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
(10s later)
ssh aaaaa#xxx.xxxx.xxxx.com
(10s later)
cd /tmp/amz337/COAFALV
(10s later)
./run_engine.sh test.py
(exit)
What's wrong with my script?
Because Tcl (and thus Expect) does not change the word boundaries when variables get substituted. You are trying to log into the host named exactly:
xxx.cloud.xxx.com -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
spaces and all.
Logically, it does not make sense to put ssh options into a variable that holds the address. May I suggest:
set addr1 "xxx.cloud.xxx.com"
set addr2 "xxx.xxxx.xxxx.com"
set ssh_opts($addr1) {-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no}
set ssh_opts($addr2) {}
Then
spawn ssh {*}$ssh_opts($addr1) $usrnm#$addr1
The {*} syntax is Tcl's "splat" operator that splits a word with spaces into the individual words. See https://tcl.tk/man/tcl8.6/TclCmd/Tcl.htm rule #5.
Later, when you connect to the second machine, you're interpolating into a string, so the splat is not necessary:
send "ssh $ssh_opts($addr2) $usrnm#$addr2\r"
You might want to catch timeout events and abort the script:
expect {
timeout {error "timed-out connecting to $addr1"}
"(yes/no)?" {send "yes\r"; exp_continue}
"password: " {send "$pwd\r"}
}
At the end of your script, after the run_engine script completes, you're still connected to addr2, so expect eof will not actually detect EOF on the spawned process. You'll timeout after 10 seconds and the Expect process will exit. For tidiness, you should:
send "./run_engine.sh test.py\r"
expect "*#"
send "exit\r"
# This prompt is from addr1
expect "*#"
send "exit\r"
# _Now_ the spawned ssh process will end
expect eof
If you think the run_engine script will take longer than 10 seconds, you should adjust the timeout variable before sending that command.
Also, while developing an Expect script, you should turn on debugging:
exp_internal 1
That will show you what's going on behind the scenes, especially when it comes to seeing if your patterns are matching.

How to make expect nested in the bash script properly?

Here is the expect file which can run succesfully.
#!/usr/bin/expect -f
set host vps_ip
set user test
set loginpwd passwd
set adminpwd passwd
set timeout 300
set prompt "#|>|\\\$"
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
expect -nocase "password:"
send "$loginpwd\r"
expect eof
spawn ssh $user#$host
expect -nocase "password:"
send "$loginpwd\r"
expect -re $prompt
send "su\r"
expect "assword:"
send "$adminpwd\r"
expect -re $prompt
send "mysql -u root -pxxxx wpdatabase < /tmp/wpdatabase_backup.sql\r"
expect eof
Here is the bash file:
mysqlword="xxxx"
mysqldump -u root -p$mysqlword wpdatabase > /home/wpdatabase_backup.sql
Now i want to make the expect file nested in the bash file because the bash file is more complicated than expect file ,most lines in the bash file were omitted.
code:
#!/usr/bin/bash
mysqlword="xxxx"
mysqldump -u root -p$mysqlword wpdatabase > /home/wpdatabase_backup.sql
/usr/bin/expect <<EOD
set host vps_ip
set user test
set loginpwd passwd
set adminpwd passwd
set timeout 300
set prompt "#|>|\\\$"
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
expect -nocase "password:"
send "$loginpwd\r"
expect eof
spawn ssh $user#$host
expect -nocase "password:"
send "$loginpwd\r"
expect -re $prompt
send "su\r"
expect "assword:"
send "$adminpwd\r"
expect -re $prompt
send "mysql -u root -pxxxx wpdatabase < /tmp/wpdatabase_backup.sql\r"
expect eof
<<EOD
An error occur :
spawn scp /home/wpdatabase_backup.sql #:/tmp
ssh: Could not resolve hostname : Name or service not known
lost connection
send: spawn id exp6 not open
while executing
"send "\r""
Quote the terminator:
cat<<'EOD'
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
Result:
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
But how to make the output of result run in bash?
/usr/bin/expect <<cat<<'EOD'
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
The code can't run!
This is because in a here-document with an unquoted terminator like EOD, parameter substitution is performed by the shell. You can see that if you replace /usr/bin/expect with cat:
$ cat<<EOD
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
Result:
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql #:/tmp
So, how to avoid parameter substitution? Quote the terminator:
$ cat<<'EOD'
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp
EOD
Result:
set host vps_ip
set user test
spawn scp /home/wpdatabase_backup.sql $user#$host:/tmp

Trimming output of expect script

I am new to Expect scripts. I have Expect script written in a bash script. The script returns me the statistics I require but along with a lot of other stuff from the terminal. "Is there any way I can get precisely the output of the command only?"
I have spent a day searching various forums but didn't had any luck.
Any sort of help will be appreciated.
Stats = $(expect -c "
spawn ssh $Username#$Host
expect \"password:\"
send \"$Password\r\"
expect \"\\\\$\"
send \"ps -A | grep java\r\"
expect -re \"$USER.*\"
send \"logout\"
")
echo $Stats > someFile.txt
You can turn of logging except for the command output:
expect -c "
log_user 0
spawn ssh $Username#$Host
expect \"password:\"
send \"$Password\r\"
expect \"\\\\$\"
log_user 1
send \"ps -A | grep java\r\"
expect -re \"$USER.*\"
send \"logout\"
"

Resources