Hello Im newbie to Expect scripting. Im trying to call remote script using ssh spawn and passing commandline arguments to the remote script.But in remote script im getting null values. Please help to solve this issue. Is the problem with passing the arguments in except script?
Local Expect script
#!/usr/bin/expect
set hana_schema [lindex $argv 1]
set table [lindex $argv 2]
set condition [lindex $argv 3]
set yyyymm [lindex $argv 4]
set targetdir [lindex $argv 5]
set split [lindex $argv 6]
set timeout 120
set ip XXXX.XXX.XX.XX
set user name
set password pass
set script /path-to-script/test.sh
# here I spawn a shell that will run ssh and redirect the script to ssh's
spawn sh -c "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $user#$ip bash $hana_schema $table $condition $yyyymm $targetdir $split < $script" "$hana_schema" "$table" "$condition" "$yyyymm" "$targetdir" "$split"
expect "Password:"
send "$password\r"
# and just wait for the script to finish
expect eof
Remote script test.sh
hana_schema=$1
table=$2
condition=$3
yyyymm=$4
targetdir=$5
split=$6
echo "$hana_schema"
echo "$table"
echo "$condition"
echo "$yyyymm"
echo "$targetdir"
echo "$split"
In this case, you're just passing the expect scripts parameters through to the remote script untouched. There's really no point saving them in separate variables. Also wrapping the ssh call with sh is not needed. I'd do this:
#!/usr/bin/expect
set timeout 120
set ip XXXX.XXX.XX.XX
set user name
set password pass
set ssh_opts {-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no}
set script /path-to-script/test.sh
spawn ssh {*}$ssh_opts $user#$ip bash $script {*}$argv
expect "Password:"
send "$password\r"
expect eof
The {*} syntax expands a list into its individual elements. See http://www.tcl.tk/man/tcl8.6/TclCmd/Tcl.htm
Related
I am passing command line arguments to a shell file i.e assignRole.sh which contains an SQL command which will use these arguments like below
ssh -o StrictHostKeyChecking=no -T $key < /oracle/oracle_user/makhshif/./assignRole.sh name open_mode >> /oracle/oracle_user/dftest.txt
This gives me error and does not accept arguments of name and open_mode and gives error, but if I execute the statement outside of ssh command like:
/oracle/oracle_user/makhshif/./assignRole.sh name open_mode
This runs fine
What is the problem with ssh command and how should I adjust these parameters so these can be accepted for the shell script assignRole.sh
< /oracle/oracle_user/makhshif/./assignRole.sh
This commands sends a content of that file to stdin. So obviously it can't process variables that you haven't send to remote machine. Just preprocess your script or create a script on remote machine and call it with arguments
Though it's even easier to pass variables like this:
ssh -o StrictHostKeyChecking=no -T $key "var1=$var1 var2=$var2" < /oracle/oracle_user/makhshif/./assignRole.sh name open_mode >> /oracle/oracle_user/dftest.txt
For example my function for executing update scripts on all cluster nodes:
# functions:
ssh_exec(){
local DESCR="$1"; shift
local SCRIPT="$1"; shift
local hosts=("$#")
echo =================================================
echo = $DESCR
echo = Going to execute $SCRIPT...
read -a res -p "Enter 'skip' to skip this step or press Enter to execute: "
if [[ $res = "skip" ]]
then
echo Skipping $SCRIPT...
else
echo Executing $SCRIPT...
for host in "${hosts[#]}"
do
local cur=${!host}
echo Executing $SCRIPT on $host - $cur...
sshpass -p "$rootpass" ssh -o "StrictHostKeyChecking no" root#${cur} \
"ns1=$ns1 ns2=$ns2 search=$search zoo1=$zoo1 zoo2=$zoo2 zoo3=$zoo3 node0=$node0 pass=$pass CURIP=$cur CURHOST=$host bash -s" \
<$SCRIPT >log-$SCRIPT-$cur.log 2>&1
echo Done.
done
echo =================================================
fi
}
Then I use it like this:
read -p "Please check that Solr started successfully and Press [Enter] key to continue..."
#Solr configset and collections:
ssh_exec "Solr configset and collections" script06.sh zoo1 zoo2 zoo3
This command executes script06.sh on 3 servers (zoo1,zoo2,zoo3)
As Sayan said, using < redirects the output of running the assignRole.sh script locally, but you want to execute that script on the remote host, with the arguments.
Pass the whole command as the final argument to ssh, in quotes:
ssh -o StrictHostKeyChecking=no -T $key "/oracle/oracle_user/makhshif/./assignRole.sh name open_mode" >> /oracle/oracle_user/dftest.txt
or split into multiple lines for readability:
ssh -o StrictHostKeyChecking=no -T $key \
"/oracle/oracle_user/makhshif/./assignRole.sh name open_mode" \
>> /oracle/oracle_user/dftest.txt
I am new to using expect. I try the code below and it fails:
expect -c 'spawn ssh user#host < script.ecma3
expect Password: ;
send "******\r";
send "exit\r";'
anyone could clarify
This might help you
#!/usr/bin/expect
set timeout -1
spawn -noecho bash -c "ssh -t user#host '<here-comes-your-command>'"
expect {
-re ".*assword.*" {
exp_send "$env(PASS_WORD)\n"
exp_continue
}
}
Note :-
1) Copy script to remote host before running it. passing whole script is not good thing to do.
2) to access enviornment variables in expect , $env(variable_name) is used.
In above example , for $PASS_WORD, i used $env(PASS_WORD) .
I have written an Ansible playbook which prompts me to enter password interactively for SSH and SUDO like below.
$ ansible-playbook -i test --limit dev app_name.yml -vv --tags=stop
SSH password:
SUDO password[defaults to SSH password]:
There are various options available with Ansible like defining password in ansible_ssh_password under group_vars but it don't seem to work for me since I can't have sshpass installed in my target server nor I am allowed to make any changes to my sudoers file.
I tried to execute ansible-playbook from a little expect script below
#!/usr/bin/expect -f
set password PASSWORD
set where_to_execute [lindex $argv 0]
set which_app_to_execute [lindex $argv 1]
set what_to_execute [lindex $argv 2]
send "ansible-playbook -i test --limit $where_to_execute $which_app_to_execute -vv --tags=$what_to_execute \r"
expect "SSH password:"
send "$password \r"
expect "SUDO password*"
send "$password \r"
expect "$"
send "exit \r"
Unfortunately this is also not working may be because SSH process is not spawned by expect. Did anyone try this method and got things working. Please suggest. Thanks.
The problem with your expect scripts is that you aren't actually running the ansible command there (or any command for that matter).
You use
send "ansible-playbook -i test --limit $where_to_execute $which_app_to_execute -vv --tags=$what_to_execute \r"
which sends that string to ... nowhere as far as I know. There's nowhere for it to go.
What you want to be doing is spawning that ansible command and then using expect to communicate with it.
Something like this:
spawn ansible-playbook -i test --limit $where_to_execute $which_app_to_execute -vv --tags=$what_to_execute
You may also want to set the timeout value if the ansible command can take a little while (to prevent expect from killing it when it doesn't return quickly enough).
It works for me using the python implementation of expect. pexpect
install pexpect using pip: pip install pexpect
You can use this code as an workaround for your expect script:
#!/usr/bin/python
import pexpect
def main(args):
#Setup variables
password, where, which, what = args
cmd = "ansible-playbook -i test --limit %s %s -vv --tags=%s" % (where, which, what)
child = pexpect.spawn(cmd)
child.sendline(password)
child.expect('SSH password:')
child.sendline(password)
child.expect('SUDO password*')
child.expect(pexpect.EOF)
print child.before
if __name__ == '__main__':
main(sys.argv[1:])
This is the most simple example but it's working fine for me.
./myscript.py mypassword dev app_name.yml stop
As #Etan Reisner pointed out, the main difference between your code that isn't working and my pexpect code is the spawn ansible command. The above code on expect also works fine:
#!/usr/bin/expect -f
spawn /usr/bin/ansible -m ping myserver --ask-pass
expect "SSH password:"
send "mypassword\r"
expect "$ "
I am trying to ssh through .tcl script from ActiveState TCL 'tclsh' window.
Having WINDOWS OS system.
#!/bin/sh
# \
exec tclsh "$0" ${1+"$#"}
package require Expect
set user [lindex $argv 0]
set password [lindex $argv 1]
set DeviceIpAddr [lindex $argv 2]
set DeviceHostName [lindex $argv 3]
foreach DeviceIp $DeviceIpAddr HostName $DeviceHostName {
spawn ssh $DeviceIp
expect "login as:"
send "$user\r"
expect "Password:"
send "$password\r"
expect "$HostName ~]#"
}
I see below error while execute in tclsh(ActiveTCL)
% tclsh Test.tcl root 321 17.250.217.151 lb02va
The system cannot find the file specified.
while executing
"spawn ssh root#$DeviceIp"
("foreach" body line 3)
invoked from within
"foreach DeviceIp $DeviceIpAddr HostName $DeviceHostName {
spawn ssh root#$DeviceIp
expect "login as:"
send "$user\r"
expect "Password:"
send..."
(file "Test.tcl" line 12)
child process exited abnormally
Kindly assist me resolving this.
Thank you.
First, make sure that you have ssh installed. From the bash prompt (Mac, Linux, Cygwin) or cmd prompt (Windows), type:
ssh
If you see an error, try to fix it. The most likely cause is ssh not installed, or not in the path.
Next, in your script, you did not use the $user variable, instead you use the hard-coded root. Fix that:
spawn ssh $user#$DeviceIp
The final problem: you already specified the user name from the command line, the ssh program will not ask for user again, so you must delete these two lines:
expect "login as:"
send "$user\r"
After that, hopefully everything will go as planned.
I have made two scripts:
This one fetches IP address & Hostnames:
#!/bin/bash
for i in `cat ~/script/hosts.txt`
do HOSTNAME=`echo $i|awk -F: '{print $1}'`
IP=`echo $i|awk -F: '{print $2}'`
TIMESTAMP=`date +%Y-%m-%d`
~/script/expect.sh $HOSTNAME $IP
done
This one does SSH into the devices:
#!/usr/bin/expect
set timeout 20
set HOSTNAME [lindex $argv 0]
set IP [lindex $argv 1]
exp_internal 1
spawn ssh -o StrictHostKeyChecking=no root#$IP
exit
I want to make a script to bakcup multiple device configurations.
Problem is that SSH is failing due to following errors:
$ ./main.sh
spawn ssh -o StrictHostKeyChecking=no root#10.102.82.235
: Name or service not knownname 10.102.82.235
spawn ssh -o StrictHostKeyChecking=no root#10.102.82.239
: Name or service not knownname 10.102.82.239
When I debug, I see the following error
spawn id exp4 sent <ssh: Could not resolve hostname 10.102.82.235\r\r: Name or
service not known\r\r\n>
: Name or service not knownname 10.102.82.235
I think the issue is due to these characters: "\r\r", "\r\r\n"
Is there any way I can filter these out?
Not an answer, but your shell script can use much improvement:
#!/bin/bash
while IFS=: read -r host ip; do
timestamp=$(date +%Y-%m-%d %T)
~/script/expect.sh "$host" "$ip"
done < ~/script/hosts.txt
Notes:
don't use for line in `cat file` to read the lines of a file -- a for loop reads words from a file
use $(...) for command substitution, not `...` -- improved readability, and easy to nest
don't use UPPERCASE_VARIABLES -- those should be reserved for the shell's use.
your (unused) TIMESTAMP variable actually contains a date, no time.
quote your "$variables" unless you can explain why you want them unquoted.