Mac OS Expect Command for Profile Removal - bash

We are replacing the profile we use for our company, and we need to run a command to remove the old profiles.
/usr/bin/profiles -D
it asks for input from the user "Are you sure you want to delete all configuration profiles? [y/n]:"
We are trying to automate this process, and have looked into the expect command but are having trouble getting it to run.
/usr/bin/expect -f - <<EOD
spawn /usr/bin/profiles -D
expect "Are you sure you want to delete all configuration profiles? [y/n]:"
send "y\n"
EOD
But we get this error when we try and run it.
sudo /Users/gpmacarthur/Desktop/test.sh
spawn /usr/bin/profiles -D
invalid command name "y/n"
while executing
"y/n"
invoked from within
"expect "Are you sure you want to delete all configuration profiles? [y/n]:""
Can anyone please help us, we'd really appreciate it.

Tcl's [...] is the command substitution syntax just like $(...) in Bash.
And [...] is also special for glob patterns (or regular expressions).
So you should write like this:
/usr/bin/expect -f - << 'QUOTED-EOD'
spawn /usr/bin/profiles -D
expect "Are you sure you want to delete all configuration profiles? \\\[y/n]:"
send "y\n"
expect eof; # This is required!
QUOTED-EOD
Or you can use Tcl's {...} quote style (like Bash's single quotes '...'):
expect {Are you sure you want to delete all configuration profiles? \[y/n]:}; # The `[' still needs to be escaped.
Or just
expect {\[y/n]:}

First of all, you don't need to use expect here. You can just use the following flag:
-f, auto confirm any questions
i.e.
/usr/bin/profiles -fD
since I already typed up the expect explanation:
Square brackets are evaluated as command substitutions and also need to be escaped in regex matches. You can use the {} notation to avoid this.
/usr/bin/expect -f - <<EOD
spawn /usr/bin/profiles -D
expect {Are you sure you want to delete all configuration profiles? \[y/n]:}
send "y\n"
EOD

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

capture expect ssh output to variable

Hey am new to bash scripts and was wondering how would I capture the output of the ssh command into a bash variable? I looked around and cant seem to get it right. I have tried puts $expect_out(buffer) but when echo it says variable does not exist
I know the response should be just one line and if I want to save that into a variable response and then echo it how would I do that?
A generic idea can be something like as below.
spawn the ssh session
make proper login
Send each commands with send
Wait for desired output with expect
Example:
spawn ssh $user#$domain
expect "password" { send "$pwd\r"}
expect "#"; # This '#' is nothing but the terminal prompt
send "$cmd\r"
expect "#"
puts $expect_out(buffer); #Will print the output of the 'cmd' output now.
The word to wait for after executing the command can vary based on your system. It can be # or $ or > or :; So, make sure you are giving the correct one. Or, you can provide a generalized pattern for the prompt as such
set prompt "#|>|:|\\\$"; # We escaped the `$` symbol with backslash to match literal '$'
While using the expect after sending the commands, it can be used as
expect -re $prompt; #Using regex to match the pattern`

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.

Bash: expect + scp : Problem on multiple files

function expect_password {
expect -c "\
set timeout 90
set env(TERM)
spawn $1
expect \"password:\"
send \"$password\r\"
expect eof
"
}
expect_password "scp /home/kit.ho/folder/file1 root#$IP:/usr/bin"
The above expect_password works perfect!
However, I want to transfer multiple files in that directory, so I tried:
expect_password "scp /home/kit.ho/folder/* root#$IP:/usr/bin"
But an error comes up:
/home/kit.ho/folder/*: No such file or directory
Killed by signal 1.
It seems that expect doesn't recognize *. How can I transfer files in that way?
There is a possible answer using rsync but I can't use that.
The manpage of expect says "If program cannot be spawned successfully because exec(2) fails", so I assume that expect uses exec internally. exec doesn't call any shell to do wildcard expansion and such magic, which means that your ssh sees the asterisk and can't handle it. Have you tried to call your shell explicitely like
expect_password "sh -c \"scp /home/kit.ho/folder/* root#$IP:/usr/bin\""
(maybe you need to omit the single quotes)?
edit:
use \" instead of '
Expect is an extension of Tcl, and Tcl does not speak shell-filename-globbing natively. Rather than shoe-horning a Tcl solution withing your framework, try
set -- /home/kit.ho/folder/*
expect_password "scp $* root#$IP:/usr/bin"
Files with spaces won't work properly with this solution.
Can't you leave away the password stuff completely and work with SSH public keys?

Conditional statament inside expect command called from a bash script

I've been trying to automate some configuration backups on my cisco devices, i've already managed to do the script that accomplishes the task but I'm trying to improve it to handle errors too.
I think that's necessary to catch the errors on two steps, first just after the 'send \"$pass\r\"' to get login errors (access denied messages) and at the 'expect \": end\"' line, to be sure that the commands issued were able to pull the configuration from the device.
I've seen some ways to do it if you work on a expect script, but i want to use a bash script to be able to supply a list of devices from a .txt file.
#!/bin/bash
data=$(date +%d-%m-%Y)
dataOntem=$(date +%d-%m-%Y -d "-1 day")
hora=$(date +%d-%m-%Y-%H:%M:%S)
log=/firewall/log/bkpCisco.$data.log
user=MYUSER
pass=MYPASS
for firewall in `cat /firewall/script/firewall.cisco`
do
VAR=$(expect -c "
spawn ssh $user#$firewall
expect \"assword:\"
send \"$pass\r\"
expect \">\"
send \"ena\r\"
expect \"assword:\"
send \"$pass\r\"
expect \"#\"
send \"conf t\r\"
expect \"conf\"
send \"no pager\r\"
send \"sh run\r\"
log_file -noappend /firewall/backup/$firewall.$data.cfg.tmp
expect \": end\"
log_file
send \"pager 24\r\"
send \"exit\r\"
send \"exit\r\"
")
echo "$VAR"
done
You need alternative patterns in the expect statements where you want to catch errors. If you're looking for a specific error message you can specify that, alternatively just specify a timeout handler which will eventually trigger when the normal output fails to appear.
Eg. after send \"$pass\r\" instead of expect \">\" try:
expect \">\" {} timeout {puts stderr {Could not log in}; exit}
ie. if the expected output arrives before the timeout (default 10 sec) do nothing and continue, otherwise complain and exit from expect. You might also need an eof pattern to match the case where your ssh session ends.
Note that since you don't do any variable substitution in expect, you don't need \"\" around your strings, you can use {} or even nothing when it's one word, eg. expect conf and send {no pager}.
BTW I agree with bstpierre that this would be cleaner if you dropped bash and did the whole thing in expect, but if bash does the job that's ok.
If you don't use single quotes (expect -c '...'), then all the $variables will be substituted by bash not expect. May be easier to put the expect code in a separate file, or maybe a heredoc.

Resources