How to search certain lines in many texts in tcl

Viewed 139

I have many texts in a folder and the texts contain the following format

#version = 11111
#version = 11112
#version = 11113
#version = 11114
version = 11115

#version is the previous version and version is the newest

I want to find out the line of the newest version(11115) and its lastest version(11114) and list them in another text.

And I have used foreach and glob to find out all the texts but I don't know how to set (version = 11115)line n and the (#version = 11114) line n-1 (or other ways to find out this two lines)

I dont know how to express previous version.

foreach fileName [glob /aaatools/N*/release/version] {
set fp [open $fileName "r"]
while { [gets $fp data]>=0 } {
   {[regexp {set [^version =/]+[A-Za-z0-9_]*} $Version] | [regexp {set ??? } $previous_version] } {
        set information [$fileName] [$Version] [$previous_version]
        set fp2 [open "tool_version" w+]
        puts $fp2 $information
2 Answers

If you have a text file structured like that, you can extract the information you are looking for fairly simply. The key trick is to use regexp to both match the line and extract the interesting part. And you are advised to use a temporary variable to capture the interesting part so that non-interesting lines don't overwrite. (This is more important when there are other, extra conditions on whether a value should be accepted.)

set current ""
set previous ""

set f [open $theTextFile]
set lines [split [read $f] "\n"]
close $f

foreach line $lines {
    if {[regexp {^\s*#\s*version\s+=\s+(\d+)} $line -> val]} {
        set previous $val
    } elseif {[regexp {^\s*version\s+=\s+(\d+)} $line -> val]} {
        if {$current ne ""} {
            puts stderr "WARNING: $theTextFile has multiple current versions: $current, $val"
        }
        set current $val
    }
}

# Show what we've found
puts "current = $current"
puts "previous = $previous"

Note that this assumes that you're interested in the last matching line in each case.

This only scans a single file. If you're working with multiple files, the easiest thing to do is to convert the above code into a procedure and then call it from a loop over the input filenames.

I would be tempted to use lsearch to find the line with the current version:

set f_out [open "tool_version" w+]
foreach file [glob ...] {
    set f_in [open $file r]
    set lines [split [read $f_in] \n]
    close $f_in

    # find the index of the line with the current version
    set idx [lsearch -regexp $lines {^version *=}]
    regexp {version\s*=\s*(\d+)} [lindex $lines $idx] -> current_version
    # index arithmetic .............................vv
    regexp {version\s*=\s*(\d+)} [lindex $lines $idx-1] -> prev_version

    puts $f_out [list $file $current_version $prev_version]
}
close $f_out

Note that -> in the regexp commands has no special meaning: it's just a dummy variable name that stores the complete text matching the pattern. It has become idiomatic.


A couple of things to mention about the code you posted:

   {[regexp {set [^version =/]+[A-Za-z0-9_]*} $Version] | [regexp {set ??? } $previous_version] } {
  • missing if
  • the first argument to regexp is a regular expression, so "set" in there is part of the pattern.
  • | is the bitwise-or operator: you want the || boolean or operator. Ref expr
Related