2014-01-11: ''with open file'' is an old programming pattern or idiom that abstracts the following: 1. open a channel to a file 1. do something with the channel 1. close the channel (When I started this page, I first searched this wiki for similar pages, but still missed a couple of pages (keyword search will only get you so far): [Playing with with] and [using], both of which address the same subject.) In Tcl code, e.g. on this wiki, this procedure is usually written step-by-step, usually without error handling (and sometimes without actually closing the file): ====== set f [open $myfile] # ... $f ... close $f ====== Abstracting away such sequences will make the code less cluttered. A couple of examples: ====== proc slurp {filename} { set f [open $filename] set res [chan read $f] chan close $f set res } proc fileputs {filename str} { set f [open $filename a] chan puts $f $str chan close $f } ====== Doing it this way, one command is needed for every file action. Instead, a generic command can be used for all cases where some kind of operation is to be done on a temporarily open file: ====== proc withOpenFile args { if {[llength $args] < 3} { error {wrong # args: should be "withOpenFile channelIdName filename ?access? ?permissions? script"} } upvar 1 [lindex $args 0] channelid try { open {*}[lrange $args 1 end-1] } on ok channelid { uplevel 1 [lindex $args end] } finally { catch {chan close $channelid} } } ====== The point of having a `[try]` structure without error handling is that it ensures that the channel will be closed even if the script terminates with an exception. So, instead of ====== set filename foo.bar set mytext {foo bar} # ... set f [open $filename a] chan puts $f $mytext chan close $f # ... set f [open $filename] set result [chan read $f] chan close $f ====== you could write ====== set filename foo.bar set mytext {foo bar} # ... fileputs $filename $mytext # ... set result [slurp $filename] ====== or ====== set filename foo.bar set mytext {foo bar} # ... withOpenFile f $filename a {chan puts $f $mytext} # ... set result [withOpenFile f $filename {chan read $f}] ====== [andrewsh] 2015-03-11: Here's something similar I hacked together about three years ago. As [dbohdan] pointed out, I could probably do without redefining [proc], but anyway. I must admit I never used this code in production :) ====== #!/usr/bin/env tclsh namespace eval ::autofile { } rename ::proc ::autofile::proc rename ::open ::autofile::open set ::autofile::files(::) {} ::autofile::proc proc {name args body} { set ns [uplevel { namespace current }] if {$ns eq "::"} { set ns {} } set name $ns\::$name ::autofile::proc $name $args $body trace add execution $name leave ::autofile::autoclose } ::autofile::proc open args { set cmd [uplevel { if {[dict exists [info frame -2] proc]} { namespace which [dict get [info frame -2] proc] } }] set file [::autofile::open {*}$args] lappend ::autofile::files($cmd) $file puts "don't forget to close $::autofile::files($cmd) for $cmd" return $file } ::autofile::proc ::autofile::autoclose {cmd args} { foreach file $::autofile::files($cmd) { puts "closing $file for $cmd" ::close $file } } namespace eval ::test { proc hi args { set f [open /dev/urandom] puts [binary encode base64 [read $f 4]] } } ::test::hi puts bye ====== ** See also ** * [with-path] <> Command | Control Structure | Concept