I'm running a script on a remote server like using this command:
ssh root#host 'bash -s' < script.sh
Now I'm trying to use expect to handle the password prompt. This is the script:
#!/usr/bin/expect
set cmd [lindex $argv 0]
spawn -noecho ssh root#host $cmd
expect {
"password:" {
send "password\r"
}
}
If I run the script, it gives no output:
./ssh.exp 'bash -s' < script.sh
I know that's not the way to use ssh without password, but this is not the question right here.
UPDATE I tried the idea of glenn jackman with a simple script but it's not working. This is the script I'm using:
#!/usr/bin/expect
spawn ssh xxx#xxx
expect "*?assword:*"
send "pwd\r"
send "echo hello world"
This is the output I get:
[xxx#xxx bin]$ expect -d my.exp
expect version 5.43.0
argv[0] = expect argv[1] = -d argv[2] = my.exp
set argc 0
set argv0 "my.exp"
set argv ""
executing commands from command file my.exp
spawn ssh xxx#xxx
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {7599}
expect: does "" (spawn_id exp6) match glob pattern "*?assword:*"? no
xxx#xxx's password:
expect: does "xxx#xxx's password: " (spawn_id exp6) match glob pattern "*?assword:*"? yes
expect: set expect_out(0,string) "xxx#xxx's password: "
expect: set expect_out(spawn_id) "exp6"
expect: set expect_out(buffer) "xxx#xxx's password: "
send: sending "pwd" to { exp6 }
send: sending "echo hello world" to { exp6 }
write() failed to write anything - will sleep(1) and retry...
UPDATE I managed it to get my script to run. This is the result which works:
#!/usr/bin/expect
set user [lindex $argv 0]
set host [lindex $argv 1]
set pwd [lindex $argv 2]
spawn ssh $user#$host bash -s
expect {
"?asswor?: " {
send "$pwd\n"
}
}
while {[gets stdin line] != -1} {
send "$line\n"
}
send \004
expect {
"END_TOKEN_OF_SCRIPT" {
exit 0
}
default {
exit 1
}
}
You need to send the script you read on stdin to the remote host:
while {[gets stdin line] != -1} {
send "$line\r"
}
# then you may have to send ctrl-D to signal end of stdin
send \004
Use expect_user, as shown in the man page:
The following script reads a password, and then runs a program every hour that demands a password each time it is run. The script supplies the password so that you only have to type it once. (See the stty command which demonstrates how to turn off password echoing.)
send_user "password?\ "
expect_user -re "(.*)\n"
for {} 1 {} {
if {[fork]!=0} {sleep 3600;continue}
disconnect
spawn priv_prog
expect Password:
send "$expect_out(1,string)\r"
. . .
exit
}
Here is what I currently have, still improving it though:
#!/usr/local/bin/expect
# For debugging make the following to be line 1:
#!/usr/local/bin/expect -D 1
set timeout 20
send_user "Username?\ "
expect_user -re "(.*)\n"
set user $expect_out(1,string)
send_user "password?\ "
stty -echo
expect_user -re "(.*)\n"
stty echo
set password $expect_out(1,string)
spawn su
expect {
"Password" {send "$password\r"}
"#" {interact + return}
}
Related
I've fighting for quite while, but I'm not able to force make it work ...
Script is running on the HPUX 11.31 server ...
Tried google and test many option, but none is working ... any idea?
It seems like password is inserted, but not executed ["enter not pressed"]
Thank you very much for advise.
Home: cat MyScript.exp
#!/usr/local/bin/expect -d
set prompt {[#$] }
lassign $argv username server password
spawn /usr/bin/ssh $server -l $username ls -la /folder | /usr/bin/grep -c -i "MyFile"
expect {
"*password:*" { send "$password\r" }
}
---- debug run ----
Home: ./MyScript.exp MyUserName MyServer MyPassword
expect version 5.45
argv[0] = /usr/local/bin/expect argv[1] = -d argv[2] = ./MyScript.exp argv[3] = MyUserName argv[4] = MyServer argv[5] = MyPassword
set argc 3
set argv0 "./MyScript.exp"
set argv "MyUserName MyServer MyPassword"
executing commands from command file ./MyScript.exp
spawn /usr/bin/ssh MyServer -l MyUserName ls -la /folder | /usr/bin/grep -c -i MyFile
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {18256}
expect: does "" (spawn_id exp4) match glob pattern "*password:*"? no
MyUserName#MyServer's password:
expect: does "MyUserName#MyServer's password: " (spawn_id exp4) match glob pattern "*password:*"? yes
expect: set expect_out(0,string) "MyUserName#MyServer's password: "
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "MyUserName#MyServer's password: "
send: sending "MyPassword\r" to { exp4 }
If that's your entire script, then after you send the password, the script ends, taking the ssh connection with it. Make your last line
expect eof
That will wait for the ssh connection to end gracefully.
Not sure if this is the "expect eof" case...
I call the script from other script like this>
$PATH/iLO_checkEXP.exp $FQDNSERVER $USER $PASSWORD > /tmp/$SERVER-SHOWALL.LOG
The output of the ssh connection should be saved to the /tmp/$SERVER-SHOWALL.LOG file, so I would be able to grep for text I'm looking for.
Content of the iLO_checkEXP.exp script is>
#!/usr/local/bin/expect
set timeout 120
set hostname [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
spawn /usr/bin/ssh -l $username -oHostKeyAlgorithms=+ssh-dss $hostname
expect "*: "
send "$password\r"
send "CommandWhichProvideOutput\r"
send "exit\r"
interact
I keep getting an error on this after sending the new password. It will change the password, but not do the rest of the script. Any ideas why it keeps erroring out. I wrote a very similar script for a different device and it works prefect, but the stuff after changing the password is not needed on that device. This device will not save the password after reboot if the rest is not completed. Doing it manually through ssh works just fine so its not the cmds that is the problem, it's something with the script.
#!/usr/bin/expect
set timeout -1
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn ssh $user#$line
expect "assword:"
send "$old\r"
sleep 10
send "passwd $user\r"
expect "assword:"
send "$new\r"
expect "assword:"
send "$new\r"
sleep 10
send "grep -v users.1.password= /tmp/system.cfg > /tmp/system.cfg.new\r"
sleep 10
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print \$2}'` >> /tmp/system.cfg.new\r"
sleep 10
send "cp /tmp/system.cfg.new /tmp/system.cfg\r"
sleep 10
send "save && reboot\r"
close
expect eof
}
The full script is alot bigger with with fail-safes if the device does not respond to ssh or the original password is wrong. That one won't work though until I figure out what is wrong with this portion. I just slimmed it down to figure out where the problem is happening. Also this line seems to be the issue as it does create the system.config.new on the line before it:
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print \$2}'` >> /tmp/system.cfg.new\r"
It was and works in ssh as this:
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print $2}'` >> /tmp/system.cfg.new\r"
But sends an error because of the $2 then is view-able by expect. I was told that putting a \$2 would make it only view-able to the remote shell. Any help would be great.
Originally it had expect "star" instead of the sleep cmds. I have been trying tons of stuff out on this script and once I get it to run incorporate it in my full script. The reason I am using sleep is because "star" doesn't seem to match output and fails on the second send $new/r. With sleep it has made it alot farther. I do have a correction though. It is actually making it right up to send "save && reboot\r". I am going to eventually use your $prompt suggestion from my other question in place of sleep or expect "star". With debug on this is where it throws the error:
send: sending "save && reboot\r" to { exp4 }
expect: spawn id exp4 not open
while executing
"expect eof"
("foreach" body line 21)
invoked from within
"foreach line [split $data \n] {
if {$line eq {}} continue
spawn ssh $user#$line
expect "assword:"
send "$old\r"
sleep 3
send "passwd $user\r"
e..."
(file "./ubnt.sh" line 15)
The "save && reboot\r" will kick out the ssh connection after it saves the settings, but it doesn't seem to be getting that far. Is there an issue with &&, maybe I need to /&&.
Interact instead of close and expect eof is the answer I will update with full script when it's done. Here is the working UBNT password change script:
#!/usr/bin/expect
set timeout 30
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn ssh $user#$line
expect {
"assword:" {
send "$old\r"
expect {
"assword:" {
close
continue
}}
expect {
"*" {
send "passwd $user\r"
expect "*assword:*"
send "$new\r"
expect "*assword:*"
send "$new\r"
expect "*"
send "grep -v users.1.password= /tmp/system.cfg > /tmp/system.cfg.new\r"
expect "*"
send "echo users.1.password=`grep $user /etc/passwd | awk -F: '{print \$2}'` >> /tmp/system.cfg.new\r"
expect "*"
send "cp /tmp/system.cfg.new /tmp/system.cfg\r"
expect "*"
send "save && reboot\r"
interact
continue
}}}}
expect {
"*" {
close
continue
}}
expect eof
}
Here is the script for Mikrotik passwords:
#!/usr/bin/expect
set timeout 30
#Edit for User
set user user
#Edit for Old Password
set old oldpassword
#Edit for New Password
set new newpassword
#get IP List from iplist.txt
set f [open "/iplist.txt"]
set data [read $f]
close $f
foreach line [split $data \n] {
if {$line eq {}} continue
spawn -noecho ssh -q -o "StrictHostKeyChecking=no" $user#$line
expect {
"assword:" {
send "$old\r"
expect {
"assword:" {
close
continue
}}
expect {
"*" {
send "user set $user password=$new\r"
expect ">"
send "quit\r"
close
continue
}}}}
expect {
"*" {
close
continue
}}
expect eof
}
Dinesh mentioned that I should not use "star" in my expect commands and said that instead use:
set prompt "#|%|>|\\\$ $"
And:
expect -re $prompt
Which I will probably change. The scripts also move onto next IP in list if the device cannot be reached via ssh or if the oldpassword does not work for the device. The only other change I plan to make on both is to have it create 3 log files listing successful ips, wrong password ips, and cant reach ips. This should only be one more command before each continue, but haven't yet researched how to output a variable to a local log file.
Thanks for your help on this Dinesh.
I have written an expect script to logon to remote machine through ssh and to execute commands to get the system information details . I'm able to login onto the remote machine and run commands and get the output . I have some trouble with parsing the output . I 'm getting some unwanted text and the command that i execute as a part of output , need your help in eliminating the same and get the relevant text as output .
The expect script is posted below
#!/usr/bin/expect
set timeout 10
log_user 0
set promptEn "(>|#|%|\\$)"
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set query [lindex $argv 3]
if {[llength $argv] == 0} {
send_user "Usage: scriptname Ip-address Username password query\n"
exit 1
}
spawn ssh -q -o StrictHostKeyChecking=no "$user\#$ip"
expect {
timeout { send_user "\nFailed to get password prompt\n"; exit 1 }
eof { send_user "\nSSH failure "; exit 1 }
"*assword"
}
send "$password\r"
expect {
timeout { send_user "\nLogin failed. Password incorrect.\n"; exit 1}
#-re "$promptEn"
-re $promptEn
}
send_user "\nPassword is correct\n"
#command to be executed
send "sudo dmidecode -t system |grep -w \"$query:\"\r"
expect {
timeout2 { send_user "\nFailed to get password prompt on remote machine \n"; exit 1 }
eof { send_user "\nSSH failure "; exit 1 }
"*assword"
}
send "$password\r"
expect {
timeout
{
send_user "\n Command Failed \n"; exit 1
}
-re "$promptEn"
}
expect -re "$query:"
puts "$expect_out(buffer)"
send "exit\r"
i run the script with the command line arguments as below
expect sshtest.exp 192.168.4.42 testaccount Password#123 Manufacturer
and i get the parsed output as
Password is correct
sudo dmidecode -t system |grep -w "Manufacturer:"
Manufacturer: LENOVO
testaccount#santhosh-Lenovo-G560:~#
i just want the last but one line Manufacturer: LENOVO as the output but not the other text .
i can invoke the script as below
expect sshtest.exp 192.168.4.42 testaccount Password#123 Manufacturer|tail -2 |head -1 to get the line but i dont want to do that way as i will be missing out the error messages if the system cant be connected .
After "password is correct", add: log_user 0
Then, change
puts "$expect_out(buffer)"
to
set lines [split $expect_out(buffer) \n]
foreach line [lsearch -inline -glob $lines {*Manufacturer:*}] {
puts $line
}
I have been trying to create an expect script to automatically login to my device through telnet
If there are no multiple possibilities for the expect command , the script works fine, logs in to the device.
#!/usr/bin/expect
set timeout 20
set ip [lindex $argv 0]
set port [lindex $argv 1]
set user [lindex $argv 2]
set password [lindex $argv 3]
spawn telnet $ip $port
expect "'^]'." sleep .1;
send "\r";
sleep .1;
expect "login:"
send "$user\r"
expect "Password:"
send "$password\r";
interact
The script above works fine and logs in successfully when i pass the correct parameters. But once i add additional branches(for error handling) to the expect command , the script gets stuck at login: prompt.After some time it prints Script Error
Any help?? Erroneous script below.
#!/usr/bin/expect
set timeout 20
set ip [lindex $argv 0]
set port [lindex $argv 1]
set user [lindex $argv 2]
set password [lindex $argv 3]
spawn telnet $ip $port
expect "'^]'."
sleep .1;
send "\r";
expect
{
"login:"
{
send "$user\r"
expect "Password:"
send "$password\r";
interact
}
"host: Connection refused"
{
send_user "ERROR:EXITING!"
exit
}
}
PS: This script is to be further developed to wait for additional prompts to load different build images on the device. Only telnet(console) connection works. so ssh is not an option.
My bad.
The problem was with the curly braces. They are supposed to be at the same line as the expect command .
here is a script using expect to achieve telnet auto login. I just tried and it really works.
#!/usr/bin/expect -f
if {[llength $argv] < 1} {
puts "Usage: ./telnet.sh host";
exit 1;
}
set timeout 10
set host [lindex $argv 0]
set user "my_user"
set password "my_password"
spawn telnet -l $user $host
expect {
"?ser*" {
send "$user\n"
exp_continue
}
"?ogin*" {
send "$user\n"
exp_continue
}
"?assword*" {
send "$password\n"
interact
exit 0;
}
}
exit 1
link: https://www.oueta.com/linux/telnet-auto-login-on-linux-and-unix-with-expect/
My Expect script shows password/user in clear text and I want to hide it.
#!/usr/local/bin/expect
########################################################################################### ############
# Input: It will handle two arguments -> a device and a show command.
########################################################################################### ############
# ######### Start of Script ######################
# #### Set up Timeouts - Debugging Variables
log_user 0
set timeout 10
set userid "USER"
set password "PASS"
# ############## Get two arguments - (1) Device (2) Command to be executed
set device [lindex $argv 0]
set command [lindex $argv 1]
spawn /usr/local/bin/ssh -l $userid $device
match_max [expr 32 * 1024]
expect {
-re "RSA key fingerprint" {send "yes\r"}
timeout {puts "Host is known"}
}
expect {
-re "username: " {send "$userid\r"}
-re "(P|p)assword: " {send "$password\r"}
-re "Warning:" {send "$password\r"}
-re "Connection refused" {puts "Host error -> $expect_out(buffer)";exit}
-re "Connection closed" {puts "Host error -> $expect_out(buffer)";exit}
-re "no address.*" {puts "Host error -> $expect_out(buffer)";exit}
timeout {puts "Timeout error. Is device down or unreachable?? ssh_expect";exit}
}
expect {
-re "\[#>]$" {send "term len 0\r"}
timeout {puts "Error reading prompt -> $expect_out(buffer)";exit}
}
expect {
-re "\[#>]$" {send "$command\r"}
timeout {puts "Error reading prompt -> $expect_out(buffer)";exit}
}
expect -re "\[#>]$"
set output $expect_out(buffer)
send "exit\r"
puts "$output\r\n"
... and add -OUseBatchMode=Yes so that if there is a problem with your keys, ssh will fail immediately (you can verify exit code) rather than just falling back to password mode and hanging (as you are running interactively)
The same issue exists in any scripting language. The script can't type in your password if it doesn't know it... the easiest solution is to use passwordless ssh, using keys.
Have a look at Glenn's answer here: How can I make an expect script prompt for a password?
I was trying to do the same sort of thing and found this useful.
The following is expect code that prompts for the username and password:
send_user "Username?\ "
expect_user -re "(.*)\n"
set user $expect_out(1,string)
send_user "password?\ "
stty -echo
expect_user -re "(.*)\n"
stty echo
set password $expect_out(1,string)
Alternatively if you need multiple runs and don't want to re-enter your password each time then you could store the password in a file in your home directory with restricted permissions (0400) and read in the password from there. Then delete the password file when no longer needed.
When you send password through expect, if you do a 'ps', you can view the entire command with the password. To avoid this, you can call expect script from another bash script sending at about 500 character random string and then the password. Then on expect script you can call password as $1 variable.
Bash script example:
#!/bin/bash
string="6e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d8206e2884f8bf8418bac1ca224472c5d820"
./expectscript $string "MySuperSecretPassword"
And expect script example:
#!/usr/bin/expect -f
set timeout 20
set string [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh "user#192.168.1.1"
expect "assword"
send "$password\r"
Hope this solve your problem.