TCL parsing issue - regex

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"
}

Related

How to use regexp from matching against a list in tcl

I am new to TCL world and i was using regexp in my code. I am reading a file and printing the lines which are not in a list. Could please help me with them. I was trying the below code but it is not working.
set fp [open file r]
set pattern {pattern1 pattern2 pattern3...patternN}
while {[gets $fp line] >= 0 } {
if {![regexp -- "$pattern" $line] } {
puts $line
}
}
Thanks
Your problem statement says that you are looking to print lines not in a list.
If that's really what you need, then you shouldn't use regexp, but should use ni which means "not in":
set fp [open file r]
set pattern {pattern1 pattern2 pattern3...patternN}
while {[gets $fp line] >= 0 } {
if {$line ni $pattern} {
puts $line
}
}
If this is not what you need, then you'll need to define your regex as several patterns alternating with the | character. For example:
tclsh> regexp {ab xy} "ab"
0
tclsh> regexp {ab|xy} "ab"
1
set fp [open file r]
set pattern {pattern1|pattern2|pattern3|...|patternN}
while {[gets $fp line] >= 0 } {
if {![regexp -- $pattern $line]} {
puts $line
}
}
Another option would be to continue defining $pattern as a list of patterns, but then you'd need to iterate through all the patterns and print the line if all the patterns failed to match.
set fp [open file r]
set pattern {pattern1 pattern2 pattern3 ... patternN}
while {[gets $fp line] >= 0 } {
set match 0
foreach p $pattern {
if {[regexp -- $p $line]} {
set match 1
break
}
}
if {$match == 0} {
puts $line
}
}
While I like #ChrisHeithoff's answer, I think it is clearer when you use a helper procedure:
proc anyPatternMatches {patternList stringToCheck} {
foreach pattern $patternList {
if {[regexp -- $pattern $stringToCheck]} {
return true
}
}
return false
}
set fp [open file r]
set patterns {pattern1 pattern2 pattern3 ... patternN}
while {[gets $fp line] >= 0 } {
if {![anyPatternMatches $patterns $line]} {
puts $line
}
}

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

In Tcl, how to read in file by line and find a list of string (from another file) then append the line with a ##

I need to be able tp read in file by line and find a set of strings (from another file) that starts with the strings plus set of characters like ({ somedata }) then append the line with a ## to that block.
Here is what I have so far:
set mydir <path to my dir>
#file name file.txt with content:
~>cat file.txt
Strng00 {
some data
}
Strng021 {
some data
}
Strng02 {
some data
}
Strng03 {
some data
}
Strng_dt {
some data
}
Strng01 {
some data
}
Strng02 {
some data
}
Strng03 {
some data
}
Strng_dt {
some data
}
Strng42 {
some data
}
Strng412
--
set list { Strng01 Strng02 Strng03 Strng_dt Strng42 } # May be read in the list from another file which needs to be matched
set fileIn [lindex $argv 0]
set fileInId [open $mydir/file.txt r]
set appendLine 0
foreach item $list {
set j 0
while {[gets $fileInId line ] != -1} {
if [regexp -all -line $item $line] { set appendLine 1 }
if $appendLine {
if [regexp {^\s*\}\s*$} $line] { set appendLine 0 }
set line "## $line"
}
puts $line
}
set j 1
}
The result only shows the first entry of the list:
Strng00
Strng021
Strng02
Strng03
Strng_dt
##Strng01 {
## some data
##}
Strng02
Strng03
Strng_dt
Strng42
Strng412
I'd like to get ## after each of the items listed..
Thanks in advance.`
Here's another take:
while {[gets $fh line] != -1} {
set first [lindex [split [string trimleft $line]] 0]
puts [format "%s%s" [expr {$first in $list ? "##" : ""}] $line]
}
That finds the first word in the line, and checks if it is an element in $list.
Does this solve your problem?
set list { Strng01 Strng02 Strng03 Strng_dt Strng42 }
set fileInId [open $mydir/file.txt r]
while {[gets $fileInId line ] != -1} {
if {[regexp -line [join $list |] $line]} {
set line "## $line"
}
puts $line
}
edit: dealing with the updated specification.
This is one way to do it; it takes advantage of the fact that the lines in the file match Tcl command invocation syntax.
proc unknown {cmd args} {
set list { Strng01 Strng02 Strng03 Strng_dt Strng42 }
if {$cmd in $list} {
foreach line [split [info level 0] \n] {
puts "## $line"
}
} else {
puts [info level 0]
}
}
source file.txt
It works like this: using source on the file means that the Tcl interpreter tries to use the keywords on each line as command names, with the { ... } parts as arguments. Since the keywords aren't existing commands, the interpreter hands the invocations over to the unknown command, which we have redefined to recognize the relevant keywords and print the complete invocation ([info level 0]) with a ## prefix if they are in the list, or else just print the invocation as it is.
Documentation: chan, foreach, if, info, join, open, proc, puts, regexp, set, source, split, unknown, while

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

expect script regexp not matching

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#