expect script regexp not matching - regex

I have the following expect script and trying to match output which is returning empty lines. Can someone help with the regexp?
#!/usr/bin/expect -f
set force_conservative 1
set timeout -1
set U [lindex $argv 0]
set P [lindex $argv 1]
set P2 [lindex $argv 2]
set HOSTLIST [open hosts.list r]
set HOSTS [split [read $HOSTLIST] "\n"]
close $HOSTLIST
set CMDLIST [open cmds.list r]
set CMDS [split [read $CMDLIST] "\n"]
close $CMDLIST
spawn ssh test#test.net
expect -re ".*password.*" { send "$P2\n" }
expect -re ".*sshhost.*" {send "telnet $H\n" }
expect -re ".*sername:.*" { send "$U\n" }
expect -re ".*ass.*:.*" { send "$P\n" }
set output [open "outputfile.txt" "a+"]
expect -re ".*#" { send "$C\n" } # prompt for cisco devices
expect "$C" ;
expect -re ".*#" { set raw_outcome $expect_out(buffer) }
send "\n"
set outcome ""
set finaloutcome ""
regexp {^\s+(.*)} $raw_outcome ignore outcome
regexp { ^\S+ } $outcome finaloutcome
puts $output $finaloutcome
close $output
expect -re ".*#" { send "exit\n" }
Here is some normal output from which I am only trying to extract the first word (ABC, CZE-NW, VRT, and TXT).
Router-1#sh ip vrf
Name Default RD Interfaces
ABC 65411:111 Lo0
Lo1
CZE-NW 65411:122 Lo5
VRT 65411:2227 Lo3527
TXT 65411:443 Mu2
Router-1#

Related

Matching pattern and adding values to a list in Tcl

I wondered if I could get some advice, I'm trying to create a list by reading a file with input shown below
Example from input file
Parameter.Coil = 1
Parameter.Coil.Info = Coil
BaseUTC.TimeSet_v0 = 1
BaseUTC.TimeSet_v0.Info = TimeSet_v0
BaseUTC.TimeSet_v1 = 1
BaseUTC.TimeSet_v1.Info = TimeSet_v1
BaseUTC.TimeSet_v14 = 1
BaseUTC.TimeSet_v14.Info = TimeSet_v14
BaseUTC.TimeSet_v32 = 1
BaseUTC.TimeSet_v32.Info = TimeSet_v32
VC.MILES = 1
VC.MILES.Info = MILES_version1
I am interested in any line with prefix of "BaseUTC." and ".Info" and would like to save value after "=" in a list
Desired:
output = TimeSet_v0 TimeSet_v1 TimeSet_v14 TimeSet_v32
I've tried the following but not getting the desired output.
set input [open "[pwd]/Data/Input" r]
set list ""
while { [gets $input line] >= 0 } {
incr number
set sline [split $line "\n"]
if {[regexp -- {BaseUTC.} $sline]} {
#puts "lines = $sline"
if {[regexp -- {.Info} $sline]} {
set output [lindex [split $sline "="] 1]
lappend list $output
}}}
puts "output = $list"
close $input
I get output as
output = \ TimeSet_v0\} \ TimeSet_v1\} \ TimeSet_v14\} \ TimeSet_v32\}
Can any help identify my mistake, please.
Thank you in advance.
A lot of your code doesn't seem to match your description (Steering? I thought you were looking for BaseUTC lines?), or just makes no sense (Why split what you already know is a single line on newline characters?), but one thing that will really help simplify things is a regular expression capture group. Something like:
#!/usr/bin/env tclsh
proc process {filename} {
set in [open $filename]
set list {}
while {[gets $in line] >= 0} {
if {[regexp {^BaseUTC\..*\.Info\s+=\s+(.*)} $line -> arg]} {
lappend list $arg
}
}
close $in
return $list
}
puts [process Data/Input]
Or using wildcards instead of regular expressions:
proc process {filename} {
set in [open $filename]
set list {}
while {[gets $in line] >= 0} {
lassign [split $line =] var arg
if {[string match {BaseUTC.*.Info} [string trim $var]]} {
lappend list [string trim $arg]
}
}
close $in
return $list
}

Tcl how to replace data from input

I have a txt file
#process #AA_version #BB_version
a11 Aa/10.10-d87_1 Bb/10.57-d21_1
a15 Aa/10.15-d37_1 Bb/10.57-d28_1
a23 Aa/10.20-d51_1 Bb/10.57-d29_3
and I want to replace the version I have input
This is my code
set fp [open tool_version r]
set process [gets stdin]
while {[gets $fh line] != -1} {
if (regexp $process $line) {
dict set process1 Aa: [lindex $line 1]
dict set process1 Bb: [lindex $line 2]
puts "Aa: [lindex $line 1]"
puts "Bb: [lindex $line 2]"
set tool [gets stdin]
if ("$tool"=="Aa")
set new_version [gets stdin]
puts "Aa version : $new_version"
lset [lindex $line1] {} $new_version
}
}
close $fp
but it can't read [lindex $line1] as a variable
or I need to use regsub command?
Thanks
I think you want:
switch -- $tool {
{Aa} {lset line 1 $new_version}
{Bb} {lset line 2 $new_version}
}
Read the docs for lset:
lset varName ?index ...? newValue
The first argument is a variable name

regexp loop to find first instance of each query TCL

I have a list variable containing some values:
lappend list {query1}
{query2}
{query3}
And some data in file1 with parts of them matching the values above
query1 first data
query1 different data
query1 different data
query2 another data
query2 random data
query3 data something
query3 last data
How do I create a regexp loop that catches only the first instance found of each query and prints them out? In this case the output would be:
query1 first data
query2 another data
query3 data something
Attempted code to produce the output
set readFile1 [open file1.txt r]
while { [gets $readFile1 data] > -1 } {
for { set n 0 } { $n < [llength $list] } { incr n } {
if { [regexp "[lindex $list $n]" $data] } {
puts $data
}
}
}
close $readFile1
I tried using a for loop while reading the data from a file, but it seems to catch all values even if the -all option is not used.
You can either read the file as a whole into a variable using read command, if the text file is smaller in size. Apply the regexp for the content and we can extract the required data.
set list {query1 query2 query3}
set fp [open file1.txt r]
set data [read $fp]
close $fp
foreach elem $list {
# '-line' flag will enable the line sensitive matching
if {[regexp -line "$elem.+" $data line]} {
puts $line
}
}
If suppose the file too large to hold or if you consider run-time memory usage, then go ahead with the reading the content line by line. There we need to have control on what already matched for which you can keep an array to maintain whether the first occurrence of any query matched or not.
set list {query1 query2 query3}
set fp [open file1.txt r]
array set first_occurence {}
while {[gets $fp line]!=-1} {
foreach elem $list {
if {[info exists first_occurence($elem)]} {
continue
}
if {[regexp $elem $line]} {
set first_occurence($elem) 1
puts $line
}
}
}
close $fp
Reference : regexp
package require fileutil
set queries {query1 query2 query3}
set result {}
::fileutil::foreachLine line file1.txt {
foreach query $queries {
if {![dict exists $result $query]} {
if {[regexp $query $line]} {
dict set result $query $line
puts $line
}
}
}
}
The trick here is to store the findings in a dictionary. If there is a value corresponding to the query in the dictionary already, we don’t search for it again. This also has the advantage that the found lines are available to the script after the search and aren’t just printed out. The regexp search looks for the query string anywhere in the line: if it should only be in the beginning of the line, use regexp ^$query $line instead.
Documentation: dict, fileutil package, foreach, if, package, puts, regexp, set
Try This,
set fd [open "query_file.txt" r]
set data [read $fd]
set uniq_list ""
foreach l [split $data "\n"] {
lappend uniq_list [lindex $l 0]
}
set uniq_list [lsort -unique $uniq_list]
foreach l $uniq_list {
if {[string equal $l ""]} {
continue
}
foreach line [split $data "\n"] {
if {[regexp $l $line]} {
puts "$line"
break
}
}
}
close $fd
References: file , list , regexp
Not using regexp at all: I assume your "query"s do not contain whitespace
set list [list query1 query2 query3]
array set seen {}
set fh [open file1]
while {[gets $fh line] != -1} {
set query [lindex [split $line] 0]
if {$query in $list && $query ni [array names seen]} {
set seen($query) 1
puts $line
}
}
query1 first data
query2 another data
query3 data something

TCL parsing issue

Below is the content of my $file_path
if **{**[info exists ABC] && $ABC == "xyz"} **{**
# constraints
# TODO:
echo "do something"
}
I want the contents of this file to be printed as it is in dofile.tcl. But somehow with the below code begin and end open brackets "{" highligted in bold are getting omitted.
Result :
if [info exists ABC] && $ABC == "xyz"} **#Notice the missing { both in begin and end**
# constraints
# TODO:
echo "do something"
}
Code:
set fp [open "$file_path" r]
set file_data [read $fp]
close $fp
echo "D1 : file data is $file_data" >> abc.log
set data [split $file_data "\n"]
foreach line $data {
echo "D2: Line is $line" >> abc.log
if { ! [regexp {^\#} $line] } {
echo "$line" >> dofile.tcl
}
}
D1 prints the entire data with "{" as required while D2 omits "{"
Try using puts instead of echo, as in:
set lf [open "abc.log" w]
set tf [open "dofile.tcl" w]
set fp [open "$file_path" r]
set file_data [read $fp]
close $fp
puts $lf "D1 : file data is $file_data"
set data [split $file_data "\n"]
foreach line $data {
puts $lf "D2: Line is $line"
if { ! [regexp {^\#} $line] } {
puts $tf "$line"
}
}
close $lf
close $tf
My output looks like this:
if **{**[info exists ABC] && $ABC == "xyz"} **{**
# constraints
# TODO:
echo "do something"
}

Find the repeating string in the txt file and modifiy with samename_modified format

Please help: my txt file contains these kind of data.
I need to find the repeating string and if the string repeating then modify the post_suffix with:
a
b
st1
ver1
st2
ver2
st3
st4
st_list1
ver3
ver4
ver_list1
st1
ver5
st2
ver5
st1
ver6
Oitput file should be like this:
a
b
st1
ver1
st2
ver2
st3
st4
st_list1
ver3
ver4
ver_list1
st1_repeted1
ver5
st2_repeted1
ver5_repeted1
st1_repeted2
ver6
My code:
if {$rec == "st1"} {
#st1
incr count_set1
if {$count_set1 == 1} {
#puts $fd "$new"
} else {
set pr_st1 $rec$U$count_set1
regsub $rec $content $pr_st1 new_set
puts $fd "$new_set"
}
}
Not sure how your code exactly should work and it seems it will check only for st1
I would use something like this:
set file [open "File.txt" r]
set output [open "output.txt" w]
set wordlist ""
while {[gets $file line] != -1} {
set id [lsearch -regexp $wordlist "^$line \[0-9]+$"]
if {$id == -1} {
lappend wordlist "$line 0"
puts $output $line
puts "new"
} else {
set count [lindex [lindex $wordlist $id] 1]
incr count
puts $output "${line}_repeated$count"
set wordlist [lreplace $wordlist $id $id "$line $count"]
puts repeated
}
}
close $file
close $output
This allows you to check for any lines. If you have large files though, the processing time will shoot up.
My take on this would be
set file [lindex $argv 0]
set f_in [open $file r]
set new ${file}.new
set f_out [open $new w]
array set count {}
while {[gets $f_in line] != -1} {
if {[info exists count($line)]} {
set line [format "%s_repeated%d" $line $count($line)]
}
puts $f_out $line
incr count($line)
}
close $f_in
close $f_out
# backup the original file and move the new file into place
file link -hard $file ${file}.old
file rename -force $new $file