I am trying to ping some ip addresses in my router. I use this code:
for {set n 0} {$n < 10} {incr n} {puts [exec "ping 199.99.$n.1]}
but this will show the output.
the issue is that I don't want to see the output. I would like to send that output into another variable and the search the content of variable with "regexp" and get the result, and do the rest of the story.
but I don't know how I can do that.
Use the set command. The puts command prints it's argument.
set pingOutput [exec ping "199.99.$n.1"]
Or append if you want all IP's results in one variable.
set allPingOutput ""
for {set n 0} {$n < 10} {incr n} {
append allPingOutput [exec ping "199.99.$n.1"]
}
Try calling the ping with the -c flag:
ping -c 1 10.0.1.1
Not sure how to do it in tcl but in php for example:
It is very important to use ping -c1 <IP address> , otherwise the script will never end as the ping process never ends :)
My code uses an array of results of every IP
for {set i 2 } {$i < 10} {incr i} {
catch {if {[regexp {bytes from} [exec ping -c1 192.168.12.$i]]} {
set flag "reachable"
} else { set flag "not reachable"}
set result(192.168.12.$i) $flag
}
}
parray result
OUTPUT :
result(192.168.12.2) = reachable
result(192.168.12.3) = reachable
result(192.168.12.5) = reachable
result(192.168.12.6) = reachable
result(192.168.12.7) = reachable
result(192.168.12.9) = reachable
Instead of storing and manipulating , I used regexp .
Related
I need to write a script on Expect to read a text file that contains two columns. Column one is the site name (dns) and the second is a router interface that will be used as the source-interface when I perform an FTP function from a Cisco router. Here is the content of the text file (myfile.txt):
site1 Gi0/0/0.44
site2 GigabitEthernet0/0
site3 GigabitEthernet0/0
site4 GigabitEthernet0/0/0
site5 GigabitEthernet0/0/0
Here is my code. It is only able to read the first column.
You will see a variable named "source-int", it has not been defined anywhere yet. I just put it in the script as a placeholder.
#!/usr/bin/expect
#
#
set workingdir cisco/rtr
puts stdout "Enter TACACS Username:"
gets stdin tacuserid
system stty -echo
puts stdout "Enter TACACS password:"
gets stdin tacpswd
puts stdout "\nEnter enable password:"
gets stdin enabpswd
system stty echo
#
set RTR [open "$workingdir/myfile.txt" r]
#
while {[gets $RTR dnsname] != -1} {
if {[ string range $dnsname 0 0 ] != "#"} {
send_user "The value of the router name is $dnsname\n"
set timeout 10
set count 0
log_file -a -noappend $workingdir/session_$dnsname\_$timestamp.log
send_log "### /START-SSH-SESSION/ IP: $dnsname # [exec date] ###\n"
spawn ssh -o StrictHostKeyChecking=no -l $tacuserid $dnsname
expect -ex "TACACS Password: "
send "$tacpswd\r"
expect ">"
send "enable\r"
expect "assword: "
send "$enabpswd\r"
expect "#"
send "config t\r"
expect "#"
send "no ip ftp passive\r"
expect "#"
send "ip ftp username anonymous\r"
expect "#"
send "ip ftp source-interface $souce-int\r"
expect "#"
send "ip ftp password cisco\r"
expect "#"
send "end\r"
expect "#"
send "copy ftp://10.10.10.1/out/customer/ACL.txt flash:\r"
expect "\?"
send "\r"
expect "#"
send "end\r"
expect "#"
send "exit\r"
send_log "\n"
send_log "### /END-SSH-SESSION/ IP: $dnsname # [exec date] ###\n"
log_file
sleep 5
}
}
exit
Any assistance is greatly appreciated.
Thanks!
You can use tcl's regexp command to split the line into 2 words.
if {[regexp {(\S+)\s+(\S+)} "$dnsname" dummy dns interface] == 1} {
puts "dns=$dns interface=$interface"
}
The regexp code \s matches a whitespace character, and \S a non-whitespace character. The + suffix matches 1 or more of the previous code. The () capture the wanted parts, and they are put by the command into the variables dns and interface.
I'm having trouble writing a one-liner that will select out the numbers between the parentheses, wrap it in double quotes, insert a comma, then select all the text after "USER_RULE: " up to the next double quote.
Here is a small sample of my file:
#213(1547485175) pass in quick on igb0 inet proto udp from <MGMT_HOSTS:1> to <UNRAID_IP:1> port = http keep state label "USER_RULE: Local Mgmt Services"
#174(1548683908) block return in quick on ALL_LAN inet proto tcp from <LOCAL_NETWORKS:7> to <LOCAL_BROADCAST:8> label "USER_RULE: Local Broadcast Noise"
#157(1547555119) block return in log quick on ALL_LAN inet from ! <NO_PFBLOCKER:1> to <pfB_BAD_IP_v4:55258> label "USER_RULE: pfb_Bad_IP (outbound)"
#137(1547478025) pass in quick on igb0 inet proto tcp from 192.168.1.0/24 to (self:13) port = ssh flags S/SA keep state label "USER_RULE: Anti-Lockout"
#386(1548774638) pass in quick on igb0.10 route-to (ovpnc1 10.20.48.141) inet proto udp from <MOBILE_DEVICES:5> to ! <PRIVATE_NETWORKS:3> port = https keep state label "USER_RULE: Policy Route" tag NO_WAN_EGRESS
Here's my expected output:
"1547485175",Local Mgmt Services
"1548683908",Local Broadcast Noise
"1547555119",pfb_Bad_IP (outbound)
"1547478025",Anti-Lockout
"1548774638",Policy Route
I've tried various combinations of awk, sed, and grep and I can get sort of the output I want. I just can't nail it. I'll spare you my ugly failed attempts.
$ sed 's/[^(]*(\([^)]*\).*"USER_RULE: *\([^"]*\).*/"\1",\2/' file
"1547485175",Local Mgmt Services
"1548683908",Local Broadcast Noise
"1547555119",pfb_Bad_IP (outbound)
"1547478025",Anti-Lockout
"1548774638",Policy Route
Could you please try following(It is always recommended to add your effort in your post so kindly do so as we all are here to learn).
awk '
BEGIN{
s1="\""
OFS=","
}
match($0,/\([^\)]*/){
val=substr($0,RSTART+1,RLENGTH-1)
}
match($0,/USER_RULE[^"]*/){
print s1 val s1,substr($0,RSTART+11,RLENGTH-11)
}' Input_file
Output will be as follows.
"1547485175",Local Mgmt Services
"1548683908",Local Broadcast Noise
"1547555119",pfb_Bad_IP (outbound)
"1547478025",Anti-Lockout
"1548774638",Policy Route
# File a.awk:
BEGIN { q = "\"" }
{ idx = index($0, "USER_RULE:")
rule = substr($0, idx + 11)
idx = index(rule, q) - 1
print q substr($0, 6, 10) q "," substr(rule, 1, idx)
}
Run:
$ awk -f a.awk file
"1547485175",Local Mgmt Services
"1548683908",Local Broadcast Noise
"1547555119",pfb_Bad_IP (outbound)
"1547478025",Anti-Lockout
"1548774638",Policy Route
When trying to write to more subprocess in Tcl, a broken pipe error will occurred. It seems that more is exited abnormally. e.g.:
#!/usr/bin/env tclsh
set pager [open {| more} w]
for {set i 0} {$i < 10000} {incr i} {
puts $pager "$i foo"
}
close $pager
Can not figure out what's going wrong.
BTW, it seems OK if less is used instead of more.
Edit: Following Python version works properly:
#!/usr/bin/env python3
import os
pager = os.popen("more", "w")
for i in range(10000):
pager.write("{} foo".format(i))
pager.close()
The issue is that when you close the pipe, the more process gets a SIGPIPE signal that makes it exit, and Tcl detects that there was an exit due to a signal and produces that error.
The simplest method of dealing with it is to put a catch around the close.
#!/usr/bin/env tclsh
set pager [open {| more} w]
for {set i 0} {$i < 10000} {incr i} {
puts $pager "$i foo"
}
catch {close $pager}
Alternatively, use less instead of more; that doesn't exit in the same way at the end of the input pipe (by default).
I have a list of IPs that I am looping through to ssh into each of them and capture some logs. Currently, it will loop through all the IPs and do what I want, the issue occurs when it hits the last IP, after it is done with the last line, it attempts to spawn another empty line resulting in an error. (spawn ssh root#)
How can I prevent this error from happening?
myexpect.sh
set user user
set pass pass
set timeout 600
# Get the list of hosts, one per line #####
set f [open "/my/ip/list.txt"]
set hosts [split [read $f] "\n"]
close $f
# Iterate over the hosts
foreach host $hosts {
spawn ssh $user#$host
expect {
"connecting (yes/no)? " {send "yes\r"; exp_continue}
"assword: " {send "$pass\r"}
}
expect "# "
send "myscript.sh -x\r"
expect "# "
send "exit\r"
expect eof
}
myiplist.txt
172.17.255.255
172.17.255.254
...
error:
[root#172.17.255.255: ]# exit //last ip in the list
Connection to 172.17.255.255 closed.
spawn ssh root#
ssh: Could not resolve hostname : Name or service not known
expect: spawn id exp5 not open
Text files end with a newline
first line\n
...
last line\n
So when you read the whole file into a variable, then split on newlines, your list looks like this:
{first line} {...} {last line} {}
because there is an empty string after the last newline.
The idiomatic way to iterate over the lines of a file in Tcl/expect is this:
set f [open file r]
while {[gets $f host] != -1} {
do something with $host
}
close $f
Or, use the -nonewline option of the read command
set f [open file]
set hosts [split [read -nonewline $f] \n]
close $f
foreach host $hosts {...}
I'm a newbie to expect (lot's of shell programming though).
I'm trying to connect to a switch using expect to be able to dump it's running config. But somehow my send commands appear not be send to the switch.
Script:
#!/usr/bin/expect -f
proc connect {passw} {
expect {
"assword:" {
send "$passw\r"
expect {
"Prompt>" {
return 0
}
}
}
}
# timed out
return 1
}
set user [lindex $argv 0]
set passw [lindex $argv 1]
set host [lindex $argv 2]
#check if all were provided
if { $user == "" || $passw == "" || $host == "" } {
puts "Usage: <user> <passw> <host>\n"
exit 1
}
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no $user#$host
set rez [connect $passw]
if { $rez == 0 } {
send {enable\r}
send {show running\r}
exit 0
}
puts "\nError connecting to switch: $host, user: $user!\n"
exit 1
Alas when I run the script (trigged from a shell script) I can see it login to the switch and see the 'Prompt', but after that the script seems to exit, because I don't see the 'enable' and 'show running' commands being executed.
Anyone can show me how to fix this?
Richard.
You need add further expect statement after sending the commands using send.
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no $user#$host
set rez [connect $passw]
set prompt "#"
if { $rez == 0 } {
send {enable\r}
expect $prompt; # Waiting for 'prompt' to appear
send {show running\r}
expect $prompt; # Waiting for the prompt to appear
exit 0
}
Without the expect command after using send, then expect has nothing to expect from the ssh process and it will simply send the commands in a faster way which is why you are not able to get any output.
One more update :
You are using a separate proc for login purpose. If you are using a separate proc, then you are recommended to pass the spawn handle. Else, the script may fail. The reason is, instead of send/expect from the process, it will send/expect from the stdin. So, you should have used it like this,
proc connect {passw handle} {
expect {
-i $handle
"assword:" {
send "$passw\r"
expect {
-i $handle
"Prompt>" {
return 0
}
}
}
}
# timed out
return 1
}
set handle [ spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no $user#$host ]
set rez [connect $passw $handle ]
set prompt "#"
if { $rez == 0 } {
send -i $handle "enable\r"
expect -i $handle $prompt
send -i $handle "show running\r"
expect -i $handle $prompt
exit 0
}
We are saving the ssh spawn handle into the variable 'handle' and it is being used with send and expect and the flag -i is used to make them to wait for the corresponding process
#Dinesh has the main answer. A couple of other notes:
send {enable\r} -- since you use braces instead of quotes, you are sending the characters \ and r and not a carriage return.
you return 0 and 1 from connect like you're a shell programmer (not an insult, I am one too). Use Tcl's boolean values instead.
A quick rewrite:
#!/usr/bin/expect -f
set prompt "Prompt>"
proc connect {passw} {
expect "assword:"
send "$passw\r"
expect {
$::prompt {return true}
timeout {return false}
}
}
lassign $argv user passw host
#check if all were provided
if { $user == "" || $passw == "" || $host == "" } {
puts "Usage: [info script] <user> <passw> <host>"
exit 1
}
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no $user#$host
if {[connect $passw]} {
send "enable\r"
expect $::prompt
send "show running\r"
expect $::prompt
send "exit\r" # neatly close the session
expect eof
} else {
puts "\nError connecting to switch: $host, user: $user!\n"
}