grep

grep is the name of a common filesystem utility on many Unix or unix like systems.

Folklore says that the name represents a command that developers in the old days used to issue within their editor or other similar program:

g/re/p

which was a global {regular expression} print command.

Grep reads through one or more input streams (stdin or files), searching for a string of text which represents some for of a regular expression, and, depending on options provided, may produce the matching lines of input.


RS wrote this tiny, and feature-poor, emulation for use on PocketPC (but it should run elsewhere too):

proc grep {re args} {
    set files [eval glob -types f $args]
    foreach file $files {
        set fp [open $file]
        while {[gets $fp line] >= 0} {
            if [regexp -- $re $line] {
                if {[llength $files] > 1} {puts -nonewline $file:}
                puts $line
            }
        }
        close $fp
    }
}

# Test:
catch {console show}
puts "Result:\n[grep "require" "*.tcl"]"

Here's another version - I wrote this out of desperation because the real grep -f ate up all my memory, and then busted, under Cygwin (remove the leading blank in the # line for an executable script):

#!/usr/bin/env tclsh
proc main argv {
    set usage {usage: grep-f.tcl refile ?file...? > outdata}
    if {[llength $argv] < 1} {puts $usage; exit}

    set fp [open [lindex $argv 0]]
    set REset [split [string trim [read $fp]] \n]
    close $fp

    if {[llength $argv] == 1} {
        grep-f $REset stdin
    } else {
        foreach file [lrange $argv 1 end] {
            set fp [open $file]
            grep-f $REset $fp
            close $fp
        }
    }
}

proc grep-f {REset fp} {
    while {[gets $fp line] >= 0} {
        foreach RE $REset {
            if {[regexp $RE $line]} {
                puts $line
                break
            }
        }
    }
}
main $argv

JM we can change the target-file selection to use wild cards (instead of listing each file) if we take some code from RS above:

  #!/usr/bin/env tclsh
  proc main argv {
      set usage {usage: grep-f.tcl refile ?file...? > outdata}
      if {[llength $argv] < 1} {puts $usage; exit}
  
      set fp [open [lindex $argv 0]]
      set REset [split [string trim [read $fp]] \n]
      close $fp
  
      if {[llength $argv] == 1} {
          grep-f $REset stdin
      } else {
          set files [eval glob -types f [lindex $argv 1]]
          foreach file $files {
              puts "============"
              set fp [open $file]
              grep-f $REset $fp
              close $fp
          }
      }
  }
  
  proc grep-f {REset fp} {
      while {[gets $fp line] >= 0} {
          foreach RE $REset {
              if {[regexp $RE $line]} {
                  puts $line
                  break
              }
          }
      }
  }
  main $argv

(this was moved here from Example Scripts Everybody Should Have

DKF: Grep does an awful lot of different things, and implementing a full version of it is probably a worthy topic for a Wiki page to itself. Here's something to get you started...

proc grep {pattern args} {
    if {[llength $args] == 0} {
        # read from stdin
        set lnum 0
        while {[gets stdin line] >= 0} {
            incr lnum
            if {[regexp $pattern $line]} {
                puts "${lnum}:${line}"
            }
        }
    } else {
        foreach filename $args {
            set file [open $filename r]
            set lnum 0
            while {[gets $file line] >= 0} {
                incr lnum
                if {[regexp $pattern $line]} {
                    puts "${filename}:${lnum}:${line}"
                }
            }
            close $file
        }
    }
}

FP: This slightly modified code is now included in Tcllib

package require tcllib
set listOfMatches [fileutil::grep $_pattern]            ;# (stdin input)
set listOfMatches [fileutil::grep $_pattern $_fileList]

RS: Hmm.. I see duplication above... how about factoring that out, and make two little ones?

proc grep {pattern args} {
    if {[llength $args] == 0} {
        grep0 "" $pattern stdin
    } else {
        foreach filename $args {
            set file [open $filename r]
            grep0 ${filename}: $pattern $file
            close $file
        }
    }
}
proc grep0 {prefix pattern handle} {
    set lnum 0
    while {[gets $handle line] >= 0} {
        incr lnum
        if {[regexp $pattern $line]} {
            puts "$prefix${lnum}:${line}"
        }
    }
}

DKF: Adjusted to print the filename too; that's usually useful with grep...


ph: Small and simple.

proc grep {re {f stdin}} {
    for {set i 0} {[gets $f line] >= 0} {incr i} {
        if {[regexp $re $line]} {
            puts "$i:$line" 
        }
    }
}

See Also

Tcllib's fileutil::grep
a grep-like utility
A little file searcher
with GUI
Example Scripts Everybody Should Have
Hits!