Reading specific data from multiple files in Tcl

Viewed 316

I am writing a TCL script to read multiple files and search them for a line containing certain word using regexp. I have been able to search for one thing from the files. But I need to modify the script to search for multiple things in a script print the items found in one file together in one line, then the items found from another file in 2nd line. I have written this

foreach fileName [glob  /home/kartik/tclprac/*/*] {
#   puts " Directories present are: [file tail $fileName]" 
    set fp [open $fileName "r"]
    while { [gets $fp data]>=0 } {
    if {[regexp {set Date*} $data] | [regexp {set Channel* } $data] } {
    #puts "file: [file dirname $fileName] data: $data"
    set information "file: [file dirname $fileName] data: $data"
    puts $information
    set fp2 [open output.txt "a"]
    puts $fp2 $information

}
}
}

Now i am getting output as:

file: /home/kartik/tclprac/wire_3 data: set Date 02/08/2021 
file: /home/kartik/tclprac/wire_2 data: set Date 01/08/2021
file: /home/kartik/tclprac/wire_1 data: set Channel Disney 
file: /home/kartik/tclprac/wire_1 data: set Date 31/07/2021

what i want is something like

file: /home/kartik/tclprac/wire_3 data: set Date 02/08/2021 
file: /home/kartik/tclprac/wire_2 data: set Date 01/08/2021 
file: /home/kartik/tclprac/wire_1 data: set Date 31/07/2021 set Channel Disney

1 Answers

It looks to me like you want to gather the results for a single file onto a single line, rather than printing out a line for each matching line (the traditional grep tool approach) with the per-line results being separated by spaces.

We can do this, but it gets a bit clearer if we split the code up into a couple of procedures (one for processing the contents of a single file, the other for the whole job).

proc processFileContents {name contents accumulatorChannel} {
    set interesting [lmap line [split $contents "\n"] {
        if {![regexp {set (?:Date|Channel) } $line]} {
            # SKIP non-matching lines
            continue
        }
        # Trim the matching lines
        string trim $line
    }]
    # If we matched anything, print out
    if {[llength $interesting]} {
        set information "file: $name data: [join $interesting \n]"
        puts $information 
        puts $accumulatorChannel $information
    }
}

proc processFilesInDir {pattern accumulatorChannel} {
    foreach fileName [glob -nocomplain -type f $pattern] {
        set channel [open $fileName]
        set contents [read $channel]
        close $channel

        processFileContents $fileName $contents $accumulatorChannel
    }
}

set accum [open output.txt "a"]
processFilesInDir /home/kartik/tclprac/*/* $accum
close $accum

If you're using an older version of Tcl that doesn't have lmap (8.5 or before) then you can write that with foreach (as lmap is really just a collecting form of foreach; the only difference is that lmap uses a hidden temporary variable to do the accumulating):

proc processFileContents {name contents accumulatorChannel} {
    set interesting {}
    foreach line [split $contents "\n"] {
        if {![regexp {set (?:Date|Channel) } $line]} {
            # SKIP non-matching lines
            continue
        }
        # Trim the matching lines
        lappend interesting [string trim $line]
    }
    # If we matched anything, print out
    if {[llength $interesting]} {
        set information "file: $name data: [join $interesting \n]"
        puts $information 
        puts $accumulatorChannel $information
    }
}
Related