I have a bash+expect script which has to connect via ssh to the remote comp (and i can't use ssh keys, need password identification in here), read the file there, find specific line with the "hostname" (like "hostname aaaa1111") and store this hostname into the variable to be used after while. How can i get the value of the "hostname" parameter? I thought that line content will be in $expect_out(buffer) variable (so i can scan it and analyze), but it's not. My script is:
#!/bin/bash
----bash part----
/usr/bin/expect << ENDOFEXPECT
spawn bash -c "ssh root#$IP"
expect "password:"
send "xxxx\r"
expect ":~#"
send "cat /etc/rc.d/rc.local |grep hostname \r"
expect ":~#"
set line $expect_out(buffer)
puts "line = $line, expect_out(buffer) = $expect_out(buffer)"
...more script...
ENDOFEXPECT
When i try to see line variable, i see only this: line = , expect_out(buffer) = (buffer) What is the right way to get the line from the file into the variable?
Or is it possible to open the file on the remote computer with expect, scan the file and get what i need to the variable?
Here http://en.wikipedia.org/wiki/Expect there is an example:
# Send the prebuilt command, and then wait for another shell prompt.
send "$my_command\r"
expect "%"
# Capture the results of the command into a variable. This can be displayed,
set results $expect_out(buffer)
seems that it doesn't work in this case?
You might just want to try and do it all from expect, as expect can control bash.
The following should do what you've described. Not sure if this is exactly what you are trying to do.
#!/bin/sh
# the next line restarts using tclsh \
exec expect "$0" "$#"
spawn bash
send "ssh root#$IP\r"
expect "password:"
send "xxxx\r"
expect ":~#"
send "cat /etc/rc.d/rc.local |grep hostname \n"
expect ":~#"
set extractedOutput $expect_out(buffer)
set list [split $extractedOutput "\n"]
foreach line $list {
set re {(?x)
.*
(*)
-S.*
}
regexp $re $line total extractedValue
if {[info exists extractedValue] && [string length $extractedValue] > 1} {
set exportValue $extractedValue
break # We've got a match!
}
send "exit\r" # disconnect from the ssh session
if {[info exists exportValue] && [string length $exportValue] > 1}{
send "export VARIABLE $exportValue\r"
} else {
send_user "No exportValue was found - exiting\n"
send "exit\r"
close
exit 1
}
# now you can do more things in bash if you like
Related
I'm trying to write a shell function that spawns a ssh process and authentificates with a password. Then, I'd like to use the spawned process to do further stuff with expect.
Here's the function I have so far:
ssh_cmd() {
ssh $USER#$HOST 2>&1 | expect -c "
log_user 0
set timeout 5
expect password: {
send \"$PASS\n\"
sleep 2
log_user 1
}
"
}
And then I'd like to use given function in other places to interact with the ssh process like this:
ssh_cmd | expect -c "
expect '#' {
send pwd\n
send exit\n
}
expect eof
"
However, running the ssh_cmd function with -d option for expect I get the following result:
expect version 5.45.4
expect: does "" (spawn_id exp0) match glob pattern "password:"? no
ubnt#ui1's password: expect: timed out
From what I understand, the output of ssh does not get piped correctly. I know the common way to do this would be to use spawn, but that would mean the process would get killed after expect exits and I could not have a generic function that authentificates ssh sessions and keeps the process alive for further usage.
What you're designing won't work. Expect needs the process to be spawned from within the expect interpreter using the spawn command. Passing the command's stdout into expect is insufficient.
You could try this:
ssh_cmd() {
# a default bit of code if user does not provide one.
# you probably want some checking and emit an error message.
local user_code=${1:-set timeout 1; send "exit\r"; expect eof}
expect -c "
log_user 0
set timeout 5
spawn ssh -l $USER $HOST
expect password: {
send \"$PASS\n\"
sleep 2
log_user 1
}
$user_code
"
}
and then invoke it like:
ssh_cmd '
expect "#" {
send pwd\r
send exit\r
}
expect eof
'
Note that single quotes have no special meaning in expect, they are just plain characters: you probably don't want to expect the prompt to be the 3 character pattern '#'
I'm including simple Expect commands within a Bash script (I know I could be just writing a pure Expect script, but I would like to get it to work from within Bash).
The script is below:
#!/bin/bash
OUTPUT=$(expect -c '
spawn ssh mihail911#blah.org
expect "password:"
send "dog\r"
')
Upon ssh'ing to the above address, it will return something of the form mihail911's password: on the prompt, so I think my expect line is valid.
When I run this my script does not print anything. It does not even show the password: prompt. In general, even if I manually provide an incorrect password, I will receive a Incorrect password-type response prompt. Why is nothing printing and how can I get my script to execute properly?
I have tried debugging by using the -d flag and it seems to show that at least the first expect prompt is being matched properly.
In addition, what values should I expect in the OUTPUT variable? When I echo this variable, it simply prints the first the first command of the expect portion of the script and then mihail911's password:. Is this what it's supposed to be printing?
Use:
#!/bin/bash
OUTPUT=$(expect -c '
# To suppress any other form of output generated by spawned process
log_user 0
spawn ssh dinesh#xxx.xxx.xx.xxx
# To match some common prompts. Update it as per your needs.
# To match literal dollar, it is escaped with backslash
set prompt "#|>|\\$"
expect {
eof {puts "Connection rejected by the host"; exit 0}
timeout {puts "Unable to access the host"; exit 0;}
"password:"
}
send "root\r"
expect {
timeout {puts "Unable to access the host"; exit 0;}
-re $prompt
}
send "date\r"
# Matching only the date cmd output alone
expect {
timeout { puts "Unable to access the host";exit 0}
-re "\n(\[^\r]*)\r"
}
send_user "$expect_out(1,string)\n"
exit 1
')
echo "Expect's return value: $?"; # Printing value returned from 'Expect'
echo "Expect Output: $OUTPUT"
Output:
dinesh#MyPC:~/stackoverflow$ ./Meric
Expect's return value: 1
Expect Output: Wed Sep 2 09:35:14 IST 2015
dinesh#MyPC:~/stackoverflow$
I would like to run this script in a loop.
What is needs to do is read a file with in that file IP addresses like:
10.0.0.0
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
And run this script on every ip listed above here.
This is what i have:
#!/usr/bin/expect
spawn telnet 10.0.0.0
expect "User Name :"
send "username\r"
expect "Password :"
send "password\r"
expect ">"
send "4\r"
expect "*#"
exit
How do i get the script above working every IP in a txt file.
You can read the file within your expect script.
Open the file and assign the file descriptor to a variable, read each line and execute the above code you wrote.
set fildes [open "myhosts.txt" r]
set ip [gets $fildes]
while {[string length $ip] > 0} {
spawn telnet $ip
expect "User Name :"
send "username\r"
expect "Password :"
send "password\r"
expect ">"
send "4\r"
expect "*#"
exit
set ip [gets $fildes]
}
close $fildes
I'm not an expert with expect, but the first thing you need to do is change your expect script to accept arguments. It should work like this (looks like you'll need -f in #!/usr/bin/expect):
#!/usr/bin/expect -f
set ip [lindex $argv 0]
spawn telnet $ip
...
Then you can simply iterate over the list of your IPs in your bash script:
while read ip ; do
myExpectScript $ip
done < list_of_ip.txt
This is just a comment on #user3088572's answer. The idiomatic way to read a file line by line is:
set fildes [open "myhosts.txt" r]
while {[gets $fildes ip] != -1} {
# do something with $ip
}
close $fildes
What I am trying to do is to:
Create a .exp file, which will read from the *.txt file from the same directory and parse all the content in the text file into a string variable in the expect script.
Loop the string, which contains a series of host names, and excecute a series of command until the string is enumerated.
So what the script does, is read a series of hostname from a txt file in the same directory, and then read them into a string, and the .exp file will auto log into each of them and excecute a series of commands.
I have the following code written but it's not working:
#!/usr/bin/expect
set timeout 20
set user test
set password test
set fp [open ./*.txt r]
set scp [read -nonewline $fp]
close $fp
spawn ssh $user#$host
expect "password"
send "$password\r"
expect "host1"
send "$scp\r"
expect "host1"
send "exit\r"
Any help is greatly appreciated....
The code should read the contents of the two files into lists of lines, then iterate over them. It ends up like this:
# Set up various other variables here ($user, $password)
# Get the list of hosts, one per line #####
set f [open "host.txt"]
set hosts [split [read $f] "\n"]
close $f
# Get the commands to run, one per line
set f [open "commands.txt"]
set commands [split [read $f] "\n"]
close $f
# Iterate over the hosts
foreach host $hosts {
spawn ssh $user#host
expect "password:"
send "$password\r"
# Iterate over the commands
foreach cmd $commands {
expect "% "
send "$cmd\r"
}
# Tidy up
expect "% "
send "exit\r"
expect eof
close
}
You could refactor this a bit with a worker procedure or two, but that's the basic idea.
I'd refactor a bit:
#!/usr/bin/expect
set timeout 20
set user test
set password test
proc check_host {hostname} {
global user passwordt
spawn ssh $user#$hostname
expect "password"
send "$password\r"
expect "% " ;# adjust to suit the prompt accordingly
send "some command\r"
expect "% " ;# adjust to suit the prompt accordingly
send "exit\r"
expect eof
}
set fp [open commands.txt r]
while {[gets $fp line] != -1} {
check_host $line
}
close $fp
Using any of the two solutions here, I would also create a logfile that you can view at a later time. Makes it easy to troubleshoot any problems after the script is run, especially if you're configuring several hundred hosts.
Add:
log_file -a [log file name]
Before your loop.
Cheers,
K
I writing a script to read user name, password and host info from a file.
I then parse this info to get the variables. I would then like to add these variables to an expect script that reads all the ip address in my file and performs certain commands on the remote devices that I am trying to log into. The script works when it connects to a known host however What I am seeing is that there is one device that is not up and running and the system promps with the following error.
ssh: connect to host 192.168.3.2 port 22: No route to host
the file
I would like to do 2 things
1. Skip the host and move to the next host
2. log the host that is down to another file so that I can troubleshoot the network issue to that host.
Please see the script below. Please any help is greatly accepted.
#! /usr/bin/expect -f
## Read the file
set fid [open /csv_pars/employee1.csv]
set content [read $fid]
close $fid
## Split into records on newlines
set records [split $content "\n"]
## Iterate over the records
foreach rec $records {
## Split into fields on comma
set fields [split $rec ","]
## Assign fields to variables and print some out...
lassign $fields\ ipaddr username password
puts "$ipaddr"
puts "$username"
puts "$password"
if {$ipaddr == ""} continue
spawn ssh -X "$username#$ipaddr"
sleep 2
expect "password:"
sleep 2
send "$pass\r"
expect "$"
send -- "ls -l\r"
expect "$"
send -- "exit\r"
expect eof
}
You need to expect to see one of two things: the password prompt, or the error message:
spawn ssh -X "$username#$ipaddr"
expect {
-re "password: ?$" {
send "$pass\r"
expect "$"
send -- "ls -l\r"
expect "$"
send -- "exit\r"
expect eof
}
"No route to host" {
set fid [open error.log a]
puts $fid "[clock format [clock seconds]]: No route to host $ipaddr"
close $fid
}
}