Transfer environment variables with expect - linux

I am writing an expect script and need to transfer environment variables over a telnet session (which the man page proudly touts as a feature but provides no other mention).
So something like this:
#!/usr/bin/expect -c
spawn telnet 1.2.3.4
set rpath ""
expect "#" { set rpath $PATH }
where $PATH is in the environment of the remote system..any ideas?

You could easily do this by spawning bash and then issue telnet to the remote system.
I presume you want to set the path variable from the local machine to the remote machine.
#!/bin/sh
# the next line restarts using tclsh \
exec expect "$0" "$#"
set prompt "~$"
set hostname "anyhost"
spawn bash
send "echo $PATH\r"
expect {
$prompt {
set pathVariable $expect_out(buffer)
}
timeout {
send_user "path hasn't been set - exiting\n"
close
exit 1
}
}
send "telnet $hostname\r"
expect {
"Login:" {}
"telnet: " {
send_error "$argv0 couldn't login to $hostname\n"
exit 1;
}
timeout {
send_error "$argv0 couldn't login to $hostname, timeout of $timeout passed\n"
exit 1;
}
}
send "$username\r"
expect "Password:"
send "$password\r"
expect $remotePrompt
send "bash\r"
send "export PATH=$pathVariable\r"
# continue with whatever you want.

Related

PowerShell script to SSH to Multiple Linux devices and restart/reboot them

I am comfortable with writing single queries. I am new to writing bash scripts trying to automate the daily stuff. I need help on creating a PowerShell or bash script where I can SSH to multiple Linux devices with same SSH key and then reboot the devices.
I access linux devices manually with the following command in PowerShell
ssh -i C:\<path-to-the-private-key\test.ppk test#XX.X.X.XXX (X - IP Address)
Then I enter the following command
sudo reboot
It asks me to type the password and then restarts the device.
I have 100+ devices that I need to restart.
I can get a list of all IP address in a text file. How can we search for all the IP address in a text file, authenticate with the SSH private key and run the sudo command to restart the device?
Would it also be possible to throw a message if it was not able to restart a device?
Any help would be appreciated.
This is the script that I have.
testreboot.sh
#!/bin/bash
pw="test123"
hosts='IP.txt'
while read -r line; do {
/usr/bin/expect << EOF do
ssh test#"$hosts" 'sudo reboot'
expect "*?assword*"
send "%pw\r"
EOF
}
done < $hosts
IP.txt
XXX.XX.XX.XX
XXX.XX.XX.XX
XXX.XX.XX.XX
XXX.XX.XX.XX
I have Ubuntu 20.04 installed from Windows App Store. I am trying to run the testreboot.sh from PowerShell using the following command and get the following error message.
bash testreboot.sh
testreboot.sh: line 2: $'\r': command not found
testreboot.sh: line 3: $'\r': command not found
testreboot.sh: line 5: $'\r': command not found
testreboot.sh: line 16: syntax error near unexpected token `done'
testreboot.sh: line 16: `done < $hosts'
A better solution to this problem is to use something like Ansible, Chef, or Puppet to solve these multi-server coordination needs.
Here is an example in a shell script using expect to ssh to another server and login:
NOTE: When you use expect in this manner, you need to escape " and $ and
other items. If password has $, it must be escaped.
This is where after logging in and expect see a command prompt
For example:
44 [localhost.localdomain]/home/map%
This is where you would need to add sudo reboot command
-re \"$reg_ex_prompt\" {
}
Test Script:
#!/bin/sh
#
debug=0
exit_val=0
spawn_item="ssh"
destination="user_name#<IP of server to shh>"
reg_ex_prompt='\[%|>|\$|#\] $'
#
# Change -D 0 to -D 1 to debug interactivly
#
expect -D $debug -c "
spawn $spawn_item $destination
set ret 1
set timeout 20
expect {
timeout {
send_user \"Timeout reached!\"
exit \$ret
}
eof {
puts \"End of test connection reached!\"
if { \$ret == 0 } {
puts \"Connection test Successful!\"
puts \"Exiting $destination ...\"
} else {
puts \"Connection Failure!\"
}
exit \$ret
}
\"Connection refused\" {
puts \"ERROR: Trouble connecting to $device_type_parm destination $destination\"
puts \"Aborting...\"
exit \$ret
}
\"Permission denied\" {
puts \"ERROR: User name or password is incorrect for $destination\"
puts \"Aborting...\"
exit \$ret
}
\"Connection reset by peer\" {
puts \"ERROR: Trouble connecting to $destination\"
puts \"Aborting...\"
exit \$ret
}
\"you sure you want to continue connecting\" {
send \"yes\r\"
exp_continue
}
\"assword:\" {
puts \"\r\nSending password\"
send \"password\\\$\r\"
exp_continue
}
\"Choice? \" {
send \"1\r\"
exp_continue
}
\"Q. Quit\" {
send \"q\r\"
exp_continue
}
-re \"$reg_ex_prompt\" {
send \"sudo reboot\r\"
sleep 2
set ret 0
exit \$ret
}
interact
} "
# get the exit value from expect statment above
exit_val=$?

How to automate ctrl+d action in the ssh expect script in bash?

I have a usecase where I need to execute a command after connecting to the host through ssh. After the command execution, I need to perform Ctrl-D and Ctrl-M so that I can issue other commands.
I tried with using EOF but It is completely closing the session.
expect << EOF
spawn ssh -o StrictHostKeyChecking=no LocalCOMUser#$nodeIp -p $ssh_port
expect {
"password:" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
send "p#ssword\r"
expect {
">" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
set timeout 120
send "mml\r"
expect {
"<" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
send "$command1\r"
expect {
"<" {}
}
send "exit;\r"
expect {
">" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
send "exit\r"
EOF
Need a command that does action as Ctrl-d.
You can simulate pressing Ctrl-D by sending the ^D/␄ character:
send "\x04"

How test SSH connectivity with expect script

I have Linux red-hat machine - version 5.X
I want to create expect script that verify if ssh login to remote machine is successfully
my test is only to get the password prompt on remote login ( I not want to enter password )
its like to test ssh connectivity
what I created until now is the following script
so if I get password prompt then script need to return 0
if not then script need to return 1
the problem with my script is that - the script return 0 even ssh login is failed ( no password prompt )
#!/usr/bin/expect
set LOGIN [lindex $argv 0]
set PASSWORD [lindex $argv 1]
set IP [lindex $argv 2]
set timeout 10
spawn ssh -o StrictHostKeyChecking=no $LOGIN#$IP
expect -re "(Password:|word:)"
exit 0
expect {
timeout {error "incorrect password"; exit 1}
eof
}
please advice how to update my script ?
You have to enclose the condition in one expect as,
set timeout 10
spawn ssh -o StrictHostKeyChecking=no $LOGIN#$IP
expect {
timeout {puts "Timeout happened";exit 0}
eof {puts "EOF received"; exit 0}
-nocase "password:" {puts "SSH connection is alive for the host $IP"; exit 1}
}
Have a look at the here which resembles your question.
sshlogin.expect :
set timeout 60
spawn ssh [lindex $argv 1]#[lindex $argv 0]
expect "yes/no" {
send "yes\r"
expect "*?assword" { send "[lindex $argv 2]\r" }
} "*?assword" { send "[lindex $argv 2]\r" }
interact
Usage :
sshlogin.expect <host> <ssh user> <ssh password>

Execute sudo using expect inside ssh from bash

I want to create a script that automates a installation on multiple linux hosts.
I login to the hosts using ssh keys and inside the login I want to do a sudo, I am trying to use expect, which I have on the stations but I don't have on the server which runs the script.
How do I do this, this is my try, but no luck with it:
#!/bin/bash
ssh user#station04 <<EOF
expect -d -c "
send \"sudo ls\"
expect {
\"password:\" { send '1234'; exp_continue }
\"$user#\"{
send "exit\r"
}
default {exit 1}
}"
EOF
exit
The result:
send: sending "sudo ls" to { exp0 }
expect: does "" (spawn_id exp0) match glob pattern "password:"? no
expect: read eof
expect: set expect_out(spawn_id) "exp0"
expect: set expect_out(buffer) ""
argv[0] = expect argv[1] = -d argv[2] = -c argv[3] =
send "sudo ls\r"
expect {
"password:" { send '1234'; exp_continue }
"#"{
send exitr
}
default {exit 1}
}
set argc 0
set argv0 "expect"
set argv ""
A.K
What about this? <- just make sure of the expected prompts.
#!/bin/bash
expect <<'END'
spawn ssh user#station04
expect "password:"
send "$pw\r"
expect "#"
send "sudo ls\r"
END
I suggest you would use public key authentication for the ssh part, then just use something like:
ssh -t username#server-ip -C "echo sudo-password | /usr/bin/sudo -S ls"
You got the usage of expect not quite right - don't send a command; rather spawn the command and send just its input. So, your script becomes:
ssh … <<EOF
expect -d -c "
spawn sudo ls
expect -nocase password: { send 1234\r }
expect eof
"
exit
EOF

Is it possible to set exit code of 'expect'

The following bash script doesn't work because command 'expect' always return 0 regardless which exit code of the remote script /tmp/my.sh returns.
any idea to make it work? thanks.
#!/usr/bash
user=root
passwd=123456abcd
host=10.58.33.21
expect -c "
spawn ssh -o StrictHostKeyChecking=no -l $user $host bash -x /tmp/my.sh
expect {
\"assword:\" {send \"$passwd\r\"}
eof {exit $?}
}
"
case "$?" in
0) echo "Password successfully changed on $host by $user" ;;
1) echo "Failure, password unchanged" ;;
2) echo "Failure, new and old passwords are too similar" ;;
3) echo "Failure, password must be longer" ;;
*) echo "Password failed to change on $host" ;;
esac
Edited at 10:23 AM 11/27/2013
Thanks for the comments. Let me emphasis the problem once again,
The main script is supposed to run on linux server A silently, during which it invokes another script my.sh on server B unattended. The question is how to get exit code of my.sh?
That's why I cannot leverage ssl_key approach in my case, which requires at least one time configuration.
#!/usr/bin/expect
set user root
set passwd 123456abcd
set host 10.58.33.21
set result_code 255
# exp_internal 1 to see internal processing
exp_internal 0
spawn ssh -o StrictHostKeyChecking=no -l $user $host bash -x /tmp/my.sh && echo aaa0bbb || echo aaa$?bbb
expect {
"assword:" {send "$passwd\r"; exp_continue}
-re "aaa(.*)bbb" {set result_code $expect_out(1,string)}
eof {}
timeout {set result_code -1}
}
switch $result_code {
0 { puts "Password successfully changed on $host by $user" }
1 { puts "Failure, password unchanged" }
2 { puts "Failure, new and old passwords are too similar" }
3 { puts "Failure, password must be longer" }
-1 { puts "Failure, timeout" }
default { puts "Password failed to change on $host" }
}
exit $result_code

Resources