standard expect works but xargs does not - bash

I am currently trying to improve my script when using snmpwalk and wanted to incorporate using xargs as this would help me gather information from multiple devices at the same time. Taking small steps I created a simple expect file with the following code:
#!/usr/bin/expect -f
set hostname [lindex $argv 0]
spawn snmpwalk $hostname
interact
From there I created a simple bash script to test the functionality of it with the code here:
#!/bin/bash
cfg=$(cat ./ips)
for c in $cfg
do
expect ./EXPECT/01.exp $c
done
Doing this produces the proper information but takes time as it goes through each IP, one at a time.
I then commented the above code out and created the xargs version here:
#!/bin/bash
xargs -I ADDRESS -P4 expect ./EXPECT/01.exp ADDRESS < ./ips
Once I run the bash script, nothing is produced other then the command from the expect file echoing with no other action. I then looked into it and saw that you can add a "-noecho" switch to it but this didn't fix anything.
I then changed the spawn command to exec but the same issue happens and nothing is shown other then an echo of the command.
Would be interested to see what issue could be causing this.

So the answer was rather simple and a bit embarrassing. The expect file needs some type of expect statement before it will run correctly. I simply put in the following expect syntax regardless if it would see this or not and it worked fine:
expect {
"(yes/no)?" {
send "yes\r"
exp_continue
}
}
From here the script worked as expected and it began to run 4 IP's at a time.

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

Run multiple SSH sessions in parallel

I have a script that helps me update the IOS of my Cisco devices when ever I need to. It works fine and I have no issues with the script itself other then the fact that it only does one device at a time.
Is there something that I can research to make the script run asymmetrically so it can do multiple sessions at one time?
The script consists of an expect script which is setup like so:
set timeout 6
set hostname [lindex argv $0]
set password [lindex argv $1]
spawn ssh $hostname
expect "TACACS*:"
send "$password\r"
expect "#"
send "term length 0\r"
< other similar commands >
interact
The main bash script works as follows:
IP=$(cat ./iphosts)
read -p "Please enter your TACACS Password:" password
for i in $IP
do
expect 01.exp $i $password | tee -a bulk.log
done
interact
Both the expect and .sh script have a little bit more to each but those usually post script completion tasks like reporting or additional commands.
Thank you for any information that you can provide on this!
You can use the xargs tool to start a number of processes in parallel. For example:
#!/bin/sh
read -p "Please enter your TACACS Password:" password
xargs -IADDRESS -P4 expect 01.exp ADDRESS $password < ./iphosts
This uses the -P argument to xargs to run up to 4 processes at a time. You could scale up the argument to -P to run more processes in parallel.
But there's a problem here: you're calling interact in your expect script, which suggests that the script is expecting (possibly requires) interactive input from you when it is running. If this is the case, the solution presented here won't work. You would need to rewrite your expect script so that it does not require any user interaction.
You may also want to investigate a tool like Ansible which (a) does this sort of parallel execution by default and (b) has explicit support for configuring a variety of network devices.

How to send commands stored in a file when using Expect

I'm writing a script that will eventually execute a list of commands on a switch (via SSH). These commands are stored in a file and the number of commands will vary
However, I'm not sure how this can be done using Expect. I know Expect can use a while loop, but I can't find a clear example. can someone here help?
/usr/bin/expect <<EOD
spawn ssh -o StrictHostKeyChecking=no admin#$switch
expect "*Enter password for admin\:"
send "password\r"
expect "*#"
send "????"
there should be a while loop that reads line by line from a file called "commands" that looks like this
command 1
command 2
command 3
...
Extreme Networks XOS has an XML API. You can use this for executing arbitrary commands. See the ExtremeXOS XML API Developer Guide which is listed on the support documentation page.
Managing switches by expect-scripting their CLIs is often erratic and error prone, I'd recommend that you avoid doing so if possible.

expect script, puting name of file in command

I have problems with expect script. Well when I grep this file I need to put it to line under and it should look like :
/opt/ericsson/arne/bin/import.sh -f bla_bla_bla.xml -val:rall
but I don't know how to put this file in beetween this line. Because when I have put grep command in beetween in didn't work, maybe problem is -val:rall that I have after?
If someone know's how could I put name of file in File1
#!/usr/local/bin/expect --
set env(TERM) vt100
set env(SHELL) /bin/sh
set env(HOME) /usr/local/bin
set PASSWORD ter
set DUL [lindex $argv 0]
set VAR _cus_ipsec
match_max 1000
spawn ssh mashost
expect {
"assword" {send "$PASSWORD\r"}
}
expect "ran#rn23"
send -- "cd /tih/opt/bla/tih/ \r"
expect "ran#rn23"
send -- "grep -il $DUL * \r*"
expect "ran#rn23"
send -- "/opt/bl/arne/bin/imp.sh -f File1 -val:rall\r"
expect "ran#rn03"
send -- "/opt/bl/arne/b/imp.sh -f File1 -import\r"
expect "ran#rn23"
interact
Ok, thanks for the clarification, I believe I do understand what you're trying to do now.
What you need to do is change the expect statement you have after you send the grep command to one that will capture your filename. And you will probably benefit from using the regexp mode of the expect command (-re), and possibly using parenthesis to capture the filename (not used in my sample below). I do not know what are the possible filenames that you can get from your grep, so you will probably need to tweak the below quite a bit, but assuming your grep will give you a single .xml file beginning with "NAME", you could do something like the following:
send -- "grep -il $DUL * \r*"
expect -re "NAME.*\.xml"
send -- "/opt/bl/arne/bin/imp.sh -f $expect_out(0,string) -val:rall\r"
As a suggestion, you should really include some timeout options for your expect statements, and some error checking, otherwise this script will not stop if anything does not go as expected. E.g. only send if you have found what you expected, etc.
Your regexp probably will be more complicated than the one I showed you, but you can get the idea. Also, include exp_internal 1 somewhere near the top of your script to get good, solid debugging info on what your script is matching (or not matching). It will be very useful as you test it.
Let me know how that goes.

expect vs. bash read

I'm trying to use expect to talk to a bash script, but I'm missing something. My script is:
#!/bin/bash
echo -n "foo? "
read
echo $REPLY
and my expect script is:
#!/usr/bin/expect
spawn ./script.sh
expect "foo? "
send "bar\r\n"
But I never see bar when I run the expect script. What am I missing here?
Dumb me, I needed to add interact to my expect script so that it can finish "interacting" with the script:
#!/usr/bin/expect
spawn ./script.sh
expect "foo? "
send "bar\r\n"
interact
I found the answer here two minutes after asking this question.
I don't familiar well with expect syntax, but you worth try autoexpect:
autoexpect ./script.sh
It will run script.sh and after you'll finish running it an expect script script.exp will be created in current directory.
After than you can edit it if you need to tune something in it.

Resources