[Richard Suchenwirth] (2002-01-31) will have to exercise Tcl with colleagues who are familiar with the Bourne shell (/bin/sh - see [Tcl heritage] for the influences it had on Tcl!), and one task will be to replace /bin/sh scripts with equivalent Tcl scripts. To make this migration easier, I am planning to introduce some syntactic sugar ([Salt and sugar]), so familiar built-ins can still be used. Most simply with ''[interp] alias'', for which we first create a shorter version: proc alias {new args} {eval [list interp alias {} $new {}] $args} alias echo puts stdout alias read gets stdin ;# if we don't need the real [read] alias cp file copy -force alias rm file delete alias mkdir file mkdir # alias whoami subst $::tcl_platform(user) # raises cryptic error message when called with args, hence: proc whoami {} {set ::tcl_platform(user)} alias -r file readable alias -w file writable alias -x file executable Note however that this ''echo'' 's output cannot be redirected to a file, or through a pipe. Things like foo=`echo $bar | grep "^grill"` would have to be restructured: set foo [exec grep ^grill <<$bar] which also has its charms, but needs habituation. (On the other foot, a Tcl'er would prefer [regexp] anyway...) Or see below, for a more powerful reimplementation which allows > and >> redirection. ---- The following one-liner emulates a frequently used part of /bin/sh's ''test'' command: proc -f name {expr {[file exists $name] && [file type $name]=="file"}} proc -z string {expr {[string length $string]==0}} This way, /bin/sh code written like this: if [ -f $filename ] ... needs no rewriting - but be aware that this is not a complete emulation of ''test''s switches, so if [ $# -ne 1 ] ... would have to rewritten in [expr] syntax (braced condition, C operators), but we even emulate the Perlish special variable ?# : set # [llength $argv] if {${#} != 1 } ... ---- This fills most of the shell special variables: # Create some shellish variables: proc sh'specials {} { global argv set ::0 $::argv0 set n 0 foreach i $argv {set ::[incr n] $i} set ::# [llength $argv] set ::\$ [pid] set ::? 0 ;# exit status of last command } The following wrapper for [exec] emulates the behavior that errors are not propagated (do not cause evaluation to stop), but may be detected from the special $? variable: proc ! args { if [catch {eval exec $args} res] { if {[lindex $::errorCode 0]=="CHILDSTATUS"} { set ::? [lindex $::errorCode 2] } } set res } ;# and an echoing version... proc !! args {puts $args; puts [eval ! $args]} This wraps the required [clock] calls into a lookalike of UNIX's ''date'' command: proc date {args} { set cmd {clock format [clock sec]} foreach arg $args { if {$arg=="-u"} { append cmd " -gmt 1" } elseif [regexp {\+(.+)} $arg -> fmt] { append cmd " -format [list $fmt]" } else {error "usage: date ?-u? ?+%H%M%S...?"} } eval $cmd } To tell the truth, this is not a Bourne shell issue, but in sh scripts, system executables are often called which may not be present (or with different behavior) on other platforms. proc : args {set ::? 0} ;# sh's no-op - but resets exit code For the path-aware . in sh see [source]. And while we're at it, and after hinting at many more such [example scripts everybody should have], here's some more Unix toolets partly reimplemented in Tcl (want more? write more!): proc echo {string {redirector -} {file -}} { set postcmd {close $fp} switch -- $redirector { > {set fp [open $file w]} >> {set fp [open $file a]} default {set fp stdout; set postcmd ""} } puts $fp $string eval $postcmd } proc cat files { set res "" foreach file [eval glob $files] { set fp [open $file] append res [read $fp [file size $file]] close $fp } set res } Somehow my suspicion gets stronger that besides many other things, Tcl is a "lightweight operatting system" riding on top of the "real" one... See also [Unixy minitools]. ---- Here's an approximation to ''/bin/du'' which gives the disk usage in and below a given directory - but even after turning off the rounding up, it reports about 0.25% more than fileutils/du ;-(: proc du {{directory .}} { set res 0 foreach item [glob -nocomplain $directory/*] { switch -- [file type $item] { directory {incr res [du $item]} file { set res [expr {$res+([file size $item]+0)/1024}] } } } set res } ;# RS ---- Years later, here's how to mimic the Bourne shell's assignment - we [let unknown know] that a command of the pattern left=right shall assign the value ''right'' to the variable ''left'': proc know what {proc unknown args $what\n[info body unknown]} know {if [regexp (.+)=(.+) [lindex $args 0] -> left right] { return [uplevel 1 [list set $left [lreplace $args 0 0 $right]]] } } Now we can interact (or code) like this: % i=1 1 % j=2 2 % expr $i+$j 3 % k=hello world hello world % set k hello world ---- !!!!!! [Tcl and other languages] - [Arts and crafts of Tcl-Tk programming] %| [Category Language] |% !!!!!!