====== ## ******************************************************** ## ## Name: bak ## ## Description: ## Create backup files as necessary to avoid overwrites. ## ## Parameters: ## ## Usage: ## before writing to a file $fname, call: bak $fname ## and the file will not get overwritten. ## ## renames like so: .bak, .ba2, .ba3, .ba4, etc. ## ## Comments: ## proc bak { fname { levels 10 } } { if { [ catch { if { [ file exists $fname ] } { set dir [ file dirname $fname ] set files [ glob -nocomplain -path ${fname} .ba* ] set i $levels while { [ incr i -1 ] } { if { [ lsearch -exact $files ${fname}.ba$i ] > -1 } { file rename -force ${fname}.ba$i ${fname}.ba[ incr i ] incr i -1 } } if { [ file exists ${fname}.bak ] } { file rename -force ${fname}.bak ${fname}.ba2 } file rename -force $fname ${fname}.bak } } err ] } { return -code error "bak($fname $levels): $err" } } ## ******************************************************** ====== ---- ''Vince'' updated example so works even if 'fname' contains strange glob-sensitive characters (which are hard to write in the Wiki). This requires Tcl 8.3 Francois Vogel December 04 2005 The above code goes into an infinite loop if called with levels==0. I fixed it by adding: if {$levels==0} {return} ---- [LES] on Feb 15 2006: Maybe I am just doing something wrong, but the proc above doesn't really work as expected for me. So I took my own stab at it: ====== proc bak { fname { levels 5 } } { if { ![ file exists [ file normalize "$fname" ] ] } { return "$fname: no such file" } set copies [ list $fname ${fname}.bkp ] for { set i 1 } { $i <= $levels } { incr i } { lappend copies "${fname}.bkp${i}" } while { [ llength $copies ] >= 2 } { set _source [ file normalize "[ @ [ lrange $copies end-1 end ] 0 ]" ] set _target [ file normalize "[ @ [ lrange $copies end-1 end ] 1 ]" ] catch { file copy -force $_source $_target } set copies [ lreplace $copies end end ] } } ====== ---- [WJG] (05/05/13) I like the idea of this module, but I want to keep my backups kept separate from my working files. The follow modifications allow me to specify a separate backup directory. In this case it's hidden so that my desktop file manager isn't full of unwanted icons. ====== proc bak { fname args } { # set some default values set levels 5 set dir ./bak foreach {a b } $args { set a [string trimleft $a -] set $a $b } if { ! [file exists $dir] } { file mkdir $dir } if { [ file exists $fname ] } { file copy -force $fname $dir set fname $dir/[file tail $fname] set files [ glob -nocomplain -path $fname .ba* ] set i $levels while { [ incr i -1 ] } { if { [ lsearch -exact $files ${fname}.ba$i ] > -1 } { file rename -force ${fname}.ba$i ${fname}.ba[ incr i ] incr i -1 } } if { [ file exists ${fname}.bak ] } { file rename -force ${fname}.bak ${fname}.ba2 } file rename -force $fname ${fname}.bak } } set fname "./test.txt" set fp [open $fname w] puts $fp "How Now Brown Cow!" close $fp for {set i 0} {$i < 5} {incr i} { bak $fname -dir [file dirname]/.bak -levels 5 } ====== <> Backup | File