$ sed s/foo/bar/ < infile > outfilecan in Tcl be substituted either with regsub or, when like in this case, no regular expression features are needed, with string map:
% > outfile [regsub -all foo [< infile] bar]resp.
% > outfile [string map {foo bar} [< infile]]where
proc > {filename str} {set f [open $filename w]; puts $f $str; close $f} proc < {filename} {set f [open $filename]; return [read $f][close $f]}
RS 2006-09-05: Playing around, waiting for a longish run to complete:
proc sed {script input} { set sep [string index $script 1] foreach {cmd from to flag} [split $script $sep] break if {$cmd ne "s"} {error "not yet implemented"} set cmd regsub if {$flag eq "g"} {lappend cmd -all} lappend cmd $from $input $to eval $cmd }Testing:
sed s-foo-bar "A foolish idea for fools" A barlish idea for fools % sed s/foo/bar/g "A foolish idea for fools" A barlish idea for barlsAdd to it to make it come closer to the real thing:^)
EF Here is a somewhat more capable implementation. It supports substitution of a particular match only, whenever an integer is present in the flag. It also supports the GNU extension for case insensitive matching. This implementation also provides an implementation for "y" and for an extension of mine that I called "e" (as in extract) and that will extract a particular (sub)group, or the text of the regular expression when no particular index is provided.
proc ::sed {script input} { set sep [string index $script 1] foreach {cmd from to flag} [split $script $sep] break switch -- $cmd { "s" { set cmd regsub if {[string first "g" $flag]>=0} { lappend cmd -all } if {[string first "i" [string tolower $flag]]>=0} { lappend cmd -nocase } set idx [regsub -all -- {[a-zA-Z]} $flag ""] if { [string is integer -strict $idx] } { set cmd [lreplace $cmd 0 0 regexp] lappend cmd -inline -indices -all -- $from $input set res [eval $cmd] set which [lindex $res $idx] return [string replace $input [lindex $which 0] [lindex $which 1] $to] } # Most generic case lappend cmd -- $from $input $to return [eval $cmd] } "e" { set cmd regexp if { $to eq "" } { set to 0 } if {![string is integer -strict $to]} { return -error code "No proper group identifier specified for extraction" } lappend cmd -inline -- $from $input return [lindex [eval $cmd] $to] } "y" { return [string map [list $from $to] $input] } } return -code error "not yet implemented" }Try the following examples:
% sed s/FOO/bar/1i "A foolish idea for fools" A foolish idea for barls % sed y/foo/bar "A foolish idea for fools" A barlish idea for barls % sed e/(o*)ls/1 "A foolish idea for fools" oo