Skip to content

Instantly share code, notes, and snippets.

@adunsmoor
Last active October 14, 2015 00:17
Show Gist options
  • Save adunsmoor/4278182 to your computer and use it in GitHub Desktop.
Save adunsmoor/4278182 to your computer and use it in GitHub Desktop.
A threaded invocation of grep with a little GUI to display results.
!/bin/env tclsh
# Ahran's Threaded Grep (ATGrep)
#
# This code is license free
# and comes without a warranty.
# If your hard drive melts
# or network crashes
# all I ask is that you don't blame me.
#
# :-)
#
# atgrep.tcl /tmp '*.txt' Event
array set search {
root "."
files "*.txt"
pattern "Event"
textwidget ""
active false
}
if { [llength $argv] >= 3} {
set search(root) [lindex $argv 0]
set search(files) [lindex $argv 1]
set search(pattern) [lindex $argv 2]
}
proc build_gui {} {
package require Tk
global search
label .l1 -text "Directory: "
entry .dir -textvar search(root)
bind .dir <Return> {do_search; break}
label .l2 -text "File Pattern:"
entry .files -textvar search(files)
bind .files <Return> {do_search; break}
label .l3 -text "Search Pattern:"
entry .search -textvar search(pattern)
bind .search <Return> {do_search; break}
button .go -text "Search" -command {do_search}
# TODO: lots of missing validation. spaces in the file patterns should
# indicate separate file searchs, for example. Right now that'll probably
# cause a stack trace.
frame .f
text .f.text -wrap none -xscrollcommand [list .f.hs set] -yscrollcommand [list .f.vs set]
scrollbar .f.hs -orient horizontal -command [list .f.text xview]
scrollbar .f.vs -orient vertical -command [list .f.text yview]
pack .f.vs -side right -fill y
pack .f.hs -side bottom -fill x
pack .f.text -fill both -expand yes
set search(textwidget) .f.text
grid .l1 -row 0 -column 0
grid .dir -row 0 -column 1
grid .l2 -row 0 -column 2
grid .files -row 0 -column 3
grid .l3 -row 0 -column 4
grid .search -row 0 -column 5 -sticky ew
grid .go -row 0 -column 6
grid .f -row 1 -column 0 -sticky nsew -columnspan 7
grid columnconfigure . 5 -weight 1
grid rowconfigure . 1 -weight 1
wm protocol . WM_DELETE_WINDOW exit
}
# Handle output of the search asynchronously so results are visible before
# everything has been searched.
proc _get_search_results {pfd args} {
global search
if { [eof $pfd] } {
set search(active) false
return
}
set line ""
if { [gets $pfd line] > 0 } {
#TODO: make the filename clickable or something.
$search(textwidget) configure -state normal
$search(textwidget) insert end $line\n
$search(textwidget) configure -state disabled
}
}
proc do_search {} {
global search
# TODO: lots of fun and exciting things like updating a progress
# bar and disabling the search button if a search is active etc.
if { $search(active) } {
bell
return
}
# clear the text area
set search(active) true
$search(textwidget) configure -state normal
$search(textwidget) delete 1.0 end
$search(textwidget) configure -state disabled
# Here's the parallel magic.
#
# Query the system for the number of cpus
set cpus [exec grep processor /proc/cpuinfo | wc -l]
# Then xargs can break the job up in to N cpus with the -P option
# (don't forget -n, too)
#
# NOTE: hiden directories can really slow things down
set pfd [open [list | \
find $search(root) \
-name .svn -prune -o \
-name .zfs -prune -o \
-type f -name $search(files) -print0 | \
xargs -0 -P$cpus -n1 grep -Hn $search(pattern)]]
# Collect results asynchronously.
fconfigure $pfd -buffering line -blocking 0
fileevent $pfd readable [list _get_search_results $pfd]
vwait search(active)
close $pfd
}
build_gui
# entering event loop...
vwait mainloop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment