Purpose: Show how to create a temp file.
DL
Tcl doesn't have a built-in command to create a temporary file and there are no built-in variables for temp directories, so you have to do a little work.
Best is to figure out the temp directory separately. Then you can create multiple files in it.
switch $tcl_platform(platform) { unix { set tmpdir /tmp # or even $::env(TMPDIR), at times. } macintosh { set tmpdir $::env(TRASH_FOLDER) ;# a better place? } default { set tmpdir [pwd] catch {set tmpdir $::env(TMP)} catch {set tmpdir $::env(TEMP)} } }
Ingemar Hansson - Added double colons above to "env" since it isn't global declared
If you just want one temp file:
set filename [file join $tmpdir [pid]]
If you want multiple tmpfiles, consider appending things like the application name, counter, time stamp, etc. For example:
set file [file join $tmpdir $appname.[pid].[incr ::globalCounter]]
DKF - Or create in a subdirectory/subfolder
if {![file exists [file join $tmpdir [pid]]]} { file mkdir [file join $tmpdir [pid]] } set file [file join $tmpdir [pid] [incr ::globalCounter]]
Michael Schlenker - Be aware of potential race conditions while opening temp files. The linux secure programming howto has some nice examples for the linux platform: http://en.tldp.org/HOWTO/Secure-Programs-HOWTO/avoid-race.html
To be sure to get a new tempfile (and not some evil symlink) try something like:
set access [list RDWR CREAT EXCL TRUNC] set perm 0600 if {[catch {open $file $access $perm} fid ]} { # something went wrong error "Could not open tempfile." } # ok everything went well
I had some luck with the following procedure on a UNIX box. Maybe, somebody can check if it works for windows.
proc tempfile {prefix suffix} { set chars "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" set nrand_chars 10 set maxtries 10 set access [list RDWR CREAT EXCL TRUNC] set permission 0600 set channel "" set checked_dir_writable 0 set mypid [pid] for {set i 0} {$i < $maxtries} {incr i} { set newname $prefix for {set j 0} {$j < $nrand_chars} {incr j} { append newname [string index $chars \ [expr ([clock clicks] ^ $mypid) % 62]] } append newname $suffix if {[file exists $newname]} { after 1 } else { if {[catch {open $newname $access $permission} channel]} { if {!$checked_dir_writable} { set dirname [file dirname $newname] if {![file writable $dirname]} { error "Directory $dirname is not writable" } set checked_dir_writable 1 } } else { # Success return [list $newname $channel] } } } if {[string compare $channel ""]} { error "Failed to open a temporary file: $chanel" } else { error "Failed to find an unused temporary file name" } }
Igor Volobouev
Stu - My version of mkstemp(3)
proc randazAZchar {} { return [format %c [expr {int(rand() * 26) + [expr {int(rand() * 10) > 5 ? 97 : 65}]}]] } proc randazAZstring {length} { set s {} for {set i $length} {$i > 0} {incr i -1} {append s [randazAZchar]} return $s } proc makeTempFile {template} { if {[set xi [string first "X" $template]] == -1} { set n $template set p {} set rl 0 } else { set n [string range $template 0 [expr {$xi - 1}]] set p [pid] set rl [expr {[string length $template] - $xi - [string length $p]}] if {$rl < 0} { set p [string range $p 0 end-[expr {$rl * -1}]] set rl 0 } } for {set i [expr {int(pow(26, ($rl <= 6 ? $rl : 6)))}]} {$i > 0} {incr i -1} { set fn $n[randazAZstring $rl]$p if {![catch {open $fn {CREAT EXCL RDWR} 0600} fd]} { file delete $fn return $fd } if {[lindex $::errorCode 1] ne "EEXIST" || [lindex $::errorCode 0] ne "POSIX"} { break } } return -code error -errorcode $::errorCode $fd }
wld
Have a look at GUID and UUID for generating unique filenames for temp-files.
---
Tcllib now provides commands for tempdir (based on python's) and tempfile (based on the above)
http://tcllib.sourceforge.net/doc/fileutil.html