## ********************************************************
##
## 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.3Francois 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
}
