[PYK]: http://ynform.org/w/Pub/TclCmdStream makes use of a slightly-modified version of [cmdSplit] walk through a text stream, extracting Tcl commands and collecting statistics also useful for parsing config files that have the same format as a Tcl script, but where each "command" represents a configuration entry rather than a real command. example data: === data {/path/to/data path/to/moredata } ; tmpdata /path/to/tmpdata #this is a comment cache /path/to/cache /path/to/another/cache ;#this is another comment cache /path/to/a/third/cache admin_account httpdadmin fonts /path/to/fonts fonts /path/to/more/fonts === ====== #! /bin/env tclsh proc cmdStream {fh callback _state} { upvar $_state state set data {} set empty [expr ![regexp {\S} $data]] while {$empty || ![info complete $data]} { if {[eof $fh]} { if {[regexp {\S} $data]} { return -code error \ "incomplete syntax in data at line $state(linecount)" } else { return } } set newdata [gets $fh] append data \n $newdata set empty [expr ![regexp {\S} $newdata]] if {$empty} { incr state(emptycount) } incr state(linecount) } incr state(listcount) uplevel 1 [list $callback $data $_state] } proc cmdSplit {body _state} { upvar $_state state set commands {} set chunk "" foreach line [split $body "\n"] { append chunk $line if {[info complete "$chunk\n"]} { # $chunk ends in a complete Tcl command, and none of the # newlines within it end a complete Tcl command. If there # are multiple Tcl commands in $chunk, they must be # separated by semi-colons. set cmd "" foreach part [split $chunk ";"] { append cmd $part if {[info complete "$cmd\n"]} { set cmd [string trimleft $cmd] # Drop empty commands and comments if {[string match {} $cmd]} { incr state(emptycount) } elseif {[string match \#* $cmd]} { incr state(commentcount) } else { lappend commands $cmd } if {[string match \#* $cmd]} { set cmd "\#;" } else { set cmd "" } } else { # No complete command yet. # Replace semicolon and continue append cmd ";" } } set chunk "" } else { # No end of command yet. Put the newline back and continue append chunk "\n" } } if {![string match {} [string trimright $chunk]]} { return -code error "Can't parse body into a\ sequence of commands.\n\tIncomplete\ command:\n-----\n$chunk\n-----" } return $commands } proc cmd {item _state} { upvar $_state state foreach cmd [cmdSplit $item state] { puts "a command!: $cmd" } } for {set fh [open data]} {![eof $fh]} {} { cmdStream $fh cmd state } close $fh puts "total lines: $state(linecount)" puts "total lists: $state(listcount)" puts "total comments: $state(commentcount)" puts "total empty: $state(emptycount)" unset state ====== <> Parsing