Expect script is not working with SFTP with proxy command - shell

I am trying to execute SFTP command with proxy option, but it is giving error. Script sample is as below
Start
sftpCommand="sftp -o ConnectTimeout=3 -o "ProxyCommand=/usr/bin/nc --proxy-type http --proxy 192.168.20.98:3128 --proxy-auth pxuser:Password#1 %h %p" -oPort=22 user-sftp2#202.89.99.20"
/usr/bin/expect << EOD
set timeout -1
spawn /bin/sh -c $sftpCommand
expect {
"*?assword:*" {
send "$sftpPassword\r"
}
"*>*" {
send "put $datewise_dir $remoteDir\r"
}
"*>*" {
send "bye\r"
}
}
EOD
End
above script is giving error like
line 6: {--proxy-type}: command not found
spawn /bin/sh -c
/bin/sh: -c: option requires an argument

In your first line defining sftpCommand you use double quotes " inside a string which is itself enclosed in double quotes - this nesting will not work. You can fix this by changing the outer quotes to single ones ', i.e.
sftpCommand='sftp -o ConnectTimeout=3 -o "ProxyCommand=/usr/bin/nc --proxy-type http --proxy 192.168.20.98:3128 --proxy-auth pxuser:Password#1 %h %p" -oPort=22 user-sftp2#202.89.99.20'

Related

Expect Script continues execute after ping response

Hello i want To Make A script to test ping after ping success will continues execute comand. Thanks For All Your Helps
below my code
set ip [10.10.10.1,10.10.10.2]
foreach hostname $ip {
spawn ping -c 2 -i 3 -W 1 $hostname
expect { "0%" {
spawn telnet $hostname
expect "*sername:"
send "$userper\n"
expect "*assword:"
send "$passper\n"
expect "#"
send "exit\n"
expect eof
}
}
}
First, this set ip [10.10.10.1,10.10.10.2] will most likely give you invalid command name "10.10.10.1,10.10.10.2"
To make a list, use set ip {10.10.10.1 10.10.10.2} -- braces and spaces.
Next, that ping command does not require any interactivity, so don't spawn it, just exec it:
set ping_output [exec ping -c 2 -i 3 -W 1 $hostname]
And then check the output for 0% -- note, you don't want to match 100% so add a leading space to the pattern:
if {[regexp { 0%} $ping_output]} {
spawn telnet $hostname
...
}
expect is an extension of tcl so all the Tcl commands are at your disposal

Expect in Alias

I am using a Bash alias that allows me to shorten the SSH command in order for me to log into my routers. Quite trivial, but a time saver! What I would now like to do is take this a step further and fully automate the logging-in of the routers.
For example in my ~/.bashrc file I have the following entry:
sshFuncB()
{
ssh -o StrictHostKeyChecking=no superuser#$1 - | /usr/bin/expect<<EOF
set timeout 5
set send_human {.1 .3 1 .05 2}
expect {
"password: " { send -h "MYPASSWORD\r" }
"No route to host" { exit 1 }
timeout { exit 1 }
}
set timeout 2
sleep 1
expect {
"N]?" { send "y\r"; exp_continue }
timeout { exit 1 }
}
expect eof
EOF
}
alias z=sshFunc
However, when I type z myrouterhostname this does not give the desired output. I must find a way to start the SSH connection and have expect automate logging in before returning control to user.
Any ideas?
This can be done as follows,
sshFuncB()
{
expect -c "
spawn ssh -o StrictHostKeyChecking=no superuser#$1
set timeout 5
set send_human {.1 .3 1 .05 2}
expect {
\"password: \" { send -h \"MYPASSWORD\r\" }
\"No route to host\" { exit 1 }
timeout { exit 1 }
}
set timeout 2
sleep 1
expect {
\"N]?\" { send \"y\r\"; exp_continue }
timeout { exit 1 }
}
expect eof
"
}
alias z=sshFuncB
Note the use of -c flag in expect which you can refer from here of you have any doubts.
If we use double quotes for the expect code with -c flag, it will allow the bash substitutions. If you use single quotes for the same, then bash substitutions won't work. (You have used #1 inside expect, which is why I used double quotes) Since I have used double quotes for the whole expect code, we have to escape the each double quotes with backslash inside the expect statement like as follows,
expect {
# Escaping the double quote with backslash
\"password: \" {some_action_here}
}
One more update. Since this is about connecting to the router and do some of your manual operations, then it is better to have interact at the end.

Expect fails but I don't see why

I have a bash script that gets info from Heroku so that I can pull a copy of my database. That script works fine in cygwin. But to run it in cron it halts because the shell that it uses halts as Heroku's authentication through Heroku Toolbelt.
Here is my crontab:
SHELL=/usr/bin/bash
5 8-18 * * 1-5 /cygdrive/c/Users/sam/work/push_db.sh >>/cygdrive/c/Users/sam/work/output.txt
I have read the Googles and the man page within cygwin to come up with this addition:
#!/usr/bin/bash
. /home/sam.walton/.profile
echo $SHELL
curl -H "Accept: application/vnd.heroku+json; version=3" -n https://api.heroku.com/
#. $HOME/.bash_profile
echo `heroku.bat pgbackups:capture --expire`
#spawn heroku.bat pgbackups:capture --expire
expect {
"Email:" { send -- "$($HEROKU_LOGIN)\r"}
"Password (typing will be hidden):" { send -- "$HEROKU_PW\r" }
timeout { echo "timed out during login"; exit 1 }
}
sleep 2
echo "first"
curl -o latest.dump -L "$(heroku.bat pgbackups:url | dos2unix)"
Here's the output from the output.txt
/usr/bin/bash
{
"links":[
{
"rel":"schema",
"href":"https://api.heroku.com/schema"
}
]
}
Enter your Heroku credentials. Email: Password (typing will be hidden): Authentication failed. Enter your Heroku credentials. Email: Password (typing will be hidden): Authentication failed. Enter your Heroku credentials. Email: Password (typing will be hidden): Authentication failed.
As you can see it appears that the output is not getting the result of the send command as it appears it's waiting. I've done so many experiments with the credentials and the expect statements. All stop here. I've seen few examples and attempted to try those out but I'm getting fuzzy eyed which is why I'm posting here. What am I not understanding?
Thanks to comments, I'm reminded to explicitly place my env variables in .bashrc:
[[ -s $USERPROFILE/.pik/.pikrc ]] && source "$USERPROFILE/.pik/.pikrc"
export HEROKU_LOGIN=myEmailHere
export HEROKU_PW=myPWhere
My revised script per #Dinesh's excellent example is below:
. /home/sam.walton/.bashrc echo $SHELL echo $HEROKU_LOGIN curl -H "Accept: application/vnd.heroku+json; version=3" -n https://api.heroku.com/
expect -d -c " spawn heroku.bat pgbackups:capture --expire --app gw-inspector expect {
"Email:" { send -- "myEmailHere\r"; exp_continue}
"Password (typing will be hidden):" { send -- "myPWhere\r" }
timeout { puts "timed out during login"; exit 1 } } " sleep 2 echo "first"
This should work but while the echo of the variable fails, giving me a clue that the variable is not being called, I am testing hardcoding the variables directly to eliminate that as a variable. But as you can see by my output not only is the echo yielding nothing, there is no clue that any diagnostics are being passed which makes me wonder if the script is even being called to run from expect, as well as the result of the spawn command. To restate, the heroku.bat command works outside the expect closure but the results are above. The result of the command directly above is:
/usr/bin/bash
{
"links":[
{
"rel":"schema",
"href":"https://api.heroku.com/schema"
}
]
}
What am I doing wrong that will show me diagnostic notes?
If you are going to use the expect code inside your bash script, instead of calling it separately, then you should have use the -c flag option.
From your code, I assume that you have the environmental variables HEROKU_LOGIN and HEROKU_PW declared in the bashrc file.
#!/usr/bin/bash
#Your code here
expect -c "
spawn <your-executable-process-here>
expect {
# HEROKU_LOGIN & HEROKU_PW will be replaced with variable values.
"Email:" { send -- "$HEROKU_LOGIN\r";exp_continue}
"Password (typing will be hidden):" { send "$HEROKU_PW\r" }
timeout { puts"timed out during login"; exit 1 }
}
"
#Your further bash code here
You should not use echo command inside expect code. Use puts instead. The option of spawning the process inside expect code will be more robust than spawning it outside.
Notice the use of double quotes with the expect -c flag. If you use single quotes, then bash script won't do any form of substitution. So, if you need bash variable substitution, you should use double quotes for the expect with -c flag.
To know about usage of -c flag, have a look at here
If you still have any issue, you can debug by appending -d with the following way.
expect -d -c "
our code here
"

escape $ in expect script

#!/bin/bash
/usr/bin/expect << SSHLOGIN
spawn ssh -o StrictHostKeyChecking=no admin#$host
expect {
Password: {
send "Pass$word\n"
expect {
OK: {
send "xstatus\n"
send "quit\n"
}
}
}
}
SSHLOGIN
It is not able to ssh because it is not escaping the '$' character in "Pass$word\n" since $ is part of the password and there is no variable being passed. How would you escape it? I know in bash, you would add '\', but since the password is in the expect script portion, that does not work.
EDIT:
changing Pass$word\n to Pass\\\$word\n works
Here-documents are bash code, so you'd still use \$.
The expect script is TCL, so you'll need to escape the $ there too. With two levels of escaping, you get:
send "Pass\\\$word\n"

Script not logging to log file. Why?

I have an expect/Tcl script as part of my bash script that logs into a remote router. Now, for testing purposes I am trying to handle the issue of time-out's. My problem is that the expect/Tcl script is not logging to my log file, and when it does it is logging everything the SSH connection is printing to my prompt which is not what I want.
Here's my expect script:
/usr/bin/expect<<EOF
set timeout 5
set send_human {.1 .3 1 .05 2}
set myStamp [exec date +\[%d\/%m\/%Y\ \%T\]]
set log_file ~/mylogfile.log
spawn ssh -o "StrictHostKeyChecking no" "me\#$1"
expect {
"password: " { send -h "mypassword\r" }
"No route to host" { exit 1 }
timeout { send_log "\$myStamp Timed out to $1\n"]; exit 1 }
}
send -h "reboot in 1\r"
sleep 1
send -h "exit\r"
expect eof
EOF
Please bear in mind that this is part of a function within my bash script that is passed the router name, hence the argument $1.
Any ideas?
You want to use the log_file command, not set a log_file variable
log_file ~/mylogfile.log
Other notes:
Tcl has a very nice builtin command to handle time, don't need to call out to date:
set myStamp [clock format [clock seconds] -format {[%d/%m/%Y %T]}]
the # character is not special in Tcl/expect and does not need to be escaped:
spawn ssh -o "StrictHostKeyChecking no" "me#$1"
As noted, log_file logs a transcript of the session. Just to log specific messages, you can use plain Tcl:
/usr/bin/expect <<EOF
proc log_msg {msg {to_stdout no}} {
set log_line "[timestamp -format {[%Y-%m-%d %T]}] \$msg"
set fh [open ~/mylogfile.log a]
puts \$fh \$log_line
close \$fh
if {\$to_stdout} {puts \$log_line}
}
# ...
expect {
"No route to host" {
log_msg "No route to host" yes
exit 1
}
timeout { log_msg "Timed out to $1"]; exit 1 }
}
# ...
EOF
This opens and closes the log for each message, which adds a bit of overhead. If milliseconds are important, open the log in the global scope, and use the global variable holding the file hendle in the log_msg proc.

Resources