Tcl How to sort certain words in the text and take the last one - tcl

I have a text and contains
#AA_VERSION = Aa/10.10-d87_1
#AA_VERSION = Aa/10.10-d887_1
#AA_VERSION = Aa/10.10-d138_1
#AA_VERSION = Aa/10.10-d82_1
How can I sort all the #AA_VERSION = beginning and print the last one?
And if the text don't have the # beginning ,how to show space or don't have version.
Thanks for your kindly help !!

Assuming you've already got a list of the contents of the lines, what you need to do is iterate over that list and test whether the line in question matches your critera; if it does, you store that matched information in a variable. At the end of the loop, the variable will contain the last such info that was matched.
set version ""
set current ""
foreach line $lines {
if {[regexp {^(#?)AA_VERSION *= *(.+)} $line -> commented info]} {
if {$commented eq "#"} {
set version [string trim $info]
} else {
if {$current ne ""} {
puts stderr "WARNING: multiple current versions"
}
set current [string trim $info]
}
}
}
# All lines scanned; describe what we've found
if {$version eq ""} {
puts "no #AA_VERSION line"
} else {
puts "#AA_VERSION is $version"
}
if {$current eq ""} {
puts "no current AA_VERSION"
} else {
puts "current AA_VERSION is $current"
}
The classic way to get a list of all lines in a file is this procedure:
proc linesOf {filename} {
set f [open $filename]
set data [read $filename]
close $f
return [split $data "\n"]
}
set lines [linesOf "mydata.txt"]

Related

Variable in curly bracket double quote tcl

I am working this script I want script to replace the second line of my session.mvw file so I am asking input "name" if I enter 2222 as input I expect the second line of my session.mvw file as {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/222.rst"}
but instead its only giving
{GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}.
puts "Enter your name: "
#flush stdout set name [gets stdin]
set in [open session.mvw r]
# get the path to the parent directory
set hostDir [file dirname session.mvw]
set tempfile "$hostDir/temp2.txt"
# open/create a temp file
set out [open $tempfile w]
set count 0
while { [eof $in] != 1 } {
gets $in line
#set firstItem [lindex $line 0] incr count
# a match has been found...
if {$count == 2 } {
puts $out {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}
} elseif {$count == 3} {
puts $out {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}
} else {
puts $out $line
}
}
close $in
close $out
close $hostDir
# over-write the existing file
#file rename -force $tempfile session_file.mvw
Tcl's got a general rule that it doesn't do substitutions inside braces. That's usually exactly the right thing. However, in this case you need something a bit more. For these sorts of cases, there's the subst command which does do those substitutions:
puts $out [subst {GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/${name}.rst"}]
(Note: If you're generating Tcl code with subst, you're probably doing it wrong. Not that this is what you're doing in this case, but still it's a warning to all readers…)
If you want to have braces around the value, you could do this
puts $out [list "GRAPHIC_FILE_1 = \"E:/ge work/hyperview scripting/${name}.rst\""]
or this
puts $out "{GRAPHIC_FILE_1 = \"E:/ge work/hyperview scripting/${name}.rst\"}"
both of which print the string
{GRAPHIC_FILE_1 = "E:/ge work/hyperview scripting/222.rst"}

I want to search a pattern [Severity Level: Critical] in whole file in tcl

I have tried the below code, but it is checking line by line and want to check it in whole file. Please help me out in writing the correct code, once i get the pattern break it and says pattern is found else pattern is not found
set search "Severity Level: Critical"
set file [open "outputfile.txt" r]
while {[gets $file data] != -1} {
if {[string match *[string toupper $search]* [string toupper $data]] } {
puts "Found '$search' in the line '$data'"
} else {
puts "Not Found '$search' in the line '$data'"
}
}
If the file is “small” with respect to available memory (e.g., no more than a few hundred megabytes) then the easiest way to find if the string is present is to load it all in with read.
set search "Severity Level: Critical"
set f [open "thefilename.txt"]
set data [read $f]
close $f
set idx [string first $search $data]
if {$idx >= 0} {
puts "Found the search term at character $idx"
# Not quite sure what you'd do with this info...
} else {
puts "Search term not present"
}
If you want to know what line it is in, you might split the data up and then use lsearch with the right options to find it.
set search "Severity Level: Critical"
set f [open "thefilename.txt"]
set data [split [read $f] "\n"]
close $f
set lineidx [lsearch -regexp -- $data ***=$search]
if {$idx >= 0} {
puts "Found the search term at line $lineidx : [lindex $data $lineidx]"
} else {
puts "Search term not present"
}
The ***= is a special escape to say “treat the rest of the RE as literal characters” and it's ideal for the case where you can't be sure that the search term is free of RE metacharacters.
The string first command is very simple, so it's easy to use correctly and to work out whether it can do what you want. The lsearch command is not simple at all, and neither are regular expressions; determining when and how to use them is correspondingly trickier.

Tcl: how to print one set

My file to be parsed is like this
Name : John
Pin : 5400
Age : 40
Place: Korea
Amount : 4000
Name : Peter
Pin : 6700
Age : 10
Place : Japan
Amount : 3600
My tcl code is
set start "Name"
set pn "Pin"
set ag "Age"
set ag_cutoff 15
set amnt "Amount"
foreach line [split $content "\n"] {
if {[regexp $start $line]} {
set count 1
set l1 $line
}
if {[regexp $pn $line] && $count ==1} {
set pin_val [lindex $line 2]
set l2 $line
}
if {[regexp $ag $line] && $count ==1} {
set ag [lindex $line 2]
if { $ag > $ag_cutoff} {
set rep_taken 1
set l3 $line
}
if {[regexp $amnt $line] && $count ==1 && $rep_taken == 1} {
set age_val [lindex $line 2]
puts $op1 "$ag $age_val "
puts $op2 "$l1\n$l2\n$l3\n"
}
This code is fine for plots.
However, I also want to o/p a file with complete set where $ag>$ag_cutoff.
Now with puts $op3 "$l1\n$l2\n$l3\n" ---> Able to print to a file. But how to print line Place which is not evaluated. Any better way to accomplish this.
Name : John
Pin : 5400
Age : 40
Place : Korea
Amount : 4000
It would be a lot simpler to let the parsing loop just create a dictionary (this replaces your code above):
set data {}
set count 0
foreach line [split $content \n] {
if {[lindex $line 0] eq "Name"} {
incr count
}
dict set data $count [lindex $line 0] [lindex $line 2]
}
This will blow up if the first line doesn't start with "Name", or if there is a missing blank between a colon and a word, and also if a value consists of several words. All of these are easy to fix.
Here, for instance, is an expanded version that takes care of the last two problems, should they occur:
set data {}
set count 0
foreach line [split $content \n] {
set keyword [string trimright [lindex $line 0] :]
set value [string trimleft [lrange $line 1 end] {: }]
if {$keyword eq "Name"} {
incr count
}
dict set data $count $keyword $value
}
When all records are stored, one can output selected records using dictionary iteration:
set ag_cutoff 15
dict for {count record} $data {
if {[dict get $record Age] > $ag_cutoff} {
dict for {k v} $record {
puts "$k : $v"
}
}
}
This also means that you can keep adding fields to the records, and the code will still work without change.
Precautions
If the data in content has empty lines at the beginning or end, or between some lines, these methods won't work. A simple way to guard against empty or blank lines at the beginning or the end is to replace
foreach line [split $content \n] {
with
foreach line [split [string trim $content] \n] {
If empty / blank lines may occur within the data, one can use this to skip them:
foreach line [split $content \n] {
if {[string is space $line]} continue
If one is 100% sure that all data is in proper list form, it is possible (but a bit code-smelly) to use list commands like lindex on it directly. If one is less sure, or if one wants to be more correct, one should convert each line to a list before working on it:
foreach line [split $content \n] {
set line [split $line]
Documentation: dict, foreach, if, incr, lindex, lrange, puts, set, split, string

How to insert a line break and grab data inside foreach loop

I have a set of fields to parse from a file and Im doing it line by line inside a foreach loop, i want to know how i can skip a line and go to the next line
For example : if encounter a string called "ABC", i need to grab a number in the next line,
some characters "ABC"
123
The problem is I'm actually having a lot of numbers in the file but i need to grab a number, specifically the number which is after a line break after the string "ABC".
How can i do this
?
It's a bit easier to do with a while loop, reading one line at a time, since you can then easily read an extra line when you find your trigger case (assuming you don't have a run of lines with "ABC" in them):
set fd [open $theFilename]
while {[gets $fd line] >= 0} {
if {
[string match *"ABC"* $line]
&& [gets $fd line] >= 0
&& [regexp {\d+} $line -> num]
} then { # I like to use 'then' after a multi-line conditional; it's optional
puts "Found number $num after \"ABC\""
}
}
close $fd
The reason this is awkward with foreach is that it will always process the same number of elements each time through the loop.
If you're dealing with data which can have the run-of-lines issue alluded to above, you are actually better off with foreach curiously enough:
set fd [open $theFilename]
set lines [split [read $fd] \n]
close $fd
foreach line $lines {
incr idx; # Always the index of the *next* line
if {
[string match *"ABC"* $line]
&& [regexp {\d+} [lindex $lines $idx] -> num]
} then {
puts "Found number $num after \"ABC\""
}
}
This works because when you do lindex of something past the end, it produces the empty string (which won't match that simple regular expression).
You can try this simple solution
set trigger 0
set fh [open "your_file" "r"]
while {[gets $fh line] != -1} {
if {[regexp -- {"ABC"} $line]} {
incr trigger
continue
}
if {$trigger > 0} {
puts $line ; # or do something else
incr trigger -1
}
}
close $fh

File Read and Write in tcl

I saw some previous posts related to opening a file to perform read and write operations but I din't get an answer for my task. I want to append some results to a file (should create a new file if it does not exist).
However, if the file already has result then append should be skipped and move on to next search for next result. I wrote a script for this and I have problem in reading the file.
The script is something like this:
proc example {} {
set a [result1 result2 ... result n]
set op [open "sample_file" "a+"]
set file_content ""
while { ![eof $op] } {
gets $op line
lappend file_content $line
}
foreach result $a {
if {[lsearch $file_content $result] == -1} {
puts $op $result
}
}
close $op
}
Note: In this script I find variable "line" to be empty {""}. I guess I have trouble in reading the file. Please help me with this
What you forgot, is to seek to the beginning of the file before reading:
proc example {} {
set a {result1 result2 ... result n}; # <== curly braces, not square
set op [open "sample_file" "a+"]
set file_content ""
seek $op 0; # <== need to do this because of a+ mode
while { ![eof $op] } {
gets $op line
lappend file_content $line
}
foreach result $a {
if {[lsearch $file_content $result] == -1} {
puts $op $result
}
}
close $op
}
Update
You can simplify the reading (while loop and all), with one single read statement:
proc example {} {
set a {result1 result2 result3}
set op [open "sample_file" "a+"]
seek $op 0
set file_content [read $op]
foreach result $a {
if {[lsearch $file_content $result] == -1} {
puts $op $result
}
}
close $op
}
example