SS 2007-03-13:
I like to program in a functional style, so many times I end writing in Tcl and other languages something like [a [b [c [d $arg]]]]. Semantically this is like the Unix shell pipe, so I wrote the following function.
Example:
set s " supercalifragilistichespiralitoso "
pipe {string trim $s} {split * {}} {lsort -unique} {join * {}} > s \
{string length} > l
puts $s
puts $lWill output
acefghiloprstu 14
The code:
proc pipe args {
set r [uplevel 1 [lindex $args 0]]
for {set i 1} {$i < [llength $args]} {incr i} {
set e [lindex $args $i]
if {[llength $e] == 1} {
if {$e eq {>}} {
incr i
uplevel 1 [list set [lindex $args $i] $r]
} else {
set r [uplevel 1 [list $e $r]]
}
} else {
set cmd {}
set substdone 0
foreach arg $e {
if {$arg eq {*}} {
lappend cmd $r
set substdone 1
} else {
lappend cmd $arg
}
}
if {$substdone == 0} {
lappend cmd $r
}
set r [uplevel 1 $cmd]
}
}
return $r
}
set s " supercalifragilistichespiralitoso "
pipe {string trim $s} {split * {}} {lsort -unique} {join * {}} > s \
{string length} > l
puts $s
puts $lIf you read italian there is a longer article about this concept in my blog at http://antirez.com/post/49
Brian Theado: Nice! I have a slightly different version I use sometimes. Unlike yours it only works for commands that return other commands (i.e. jacl, tcom and tdom are all good candidates). Also, it actually uses a | character as the separator.
proc pipe {cmd args} {
while {[llength $args]} {
set n [lsearch -exact $args |]
if {$n < 0} { set n [llength $args]; lappend args | }
set a [lreplace $args $n end]
if {[llength $a]} {
set cmd [eval [linsert $a 0 $cmd]]
}
set args [lreplace $args 0 $n]
}
return $cmd
}I adapted the above code from some version of ratcl (my all-time favorite Tcl extension). It uses the "|" to chain together view operators.
Examples
tdom:
pipe [dom parse $xml] documentElement | firstChild | getAttribute style # Instead of: set document [dom parse $xml] set root [$document documentElement] [$root firstChild] getAttribute style tcom]: set excel [::tcom::ref createobject Excel.Application] set worksheets [pipe $excel Workbooks | Add | Worksheets] set lastWorksheet [$worksheets Item [$worksheets Count]] # Instead of: set application [::tcom::ref createobject "Excel.Application"] set workbooks [$application Workbooks] set workbook [$workbooks Add] set worksheets [$workbook Worksheets] set lastWorksheet [$worksheets Item [$worksheets Count]]
JBR 2009-11-22: I've updated the code above for 8.6 features:
proc | { cmd args } {
while { [set n [lsearch -exact $args |]] >= 0 } {
set cmd [uplevel 1 [list $cmd {*}[lrange $args 0 $n-1]]]
if { [llength [set args [lrange $args $n+1 end]]] == 0 } {
error "Null command at end of pipeline"
}
}
uplevel 1 [list $cmd {*}$args]
}