2007-Dec-02 Here is a tool to synchronize two filesystem directories, which the name of means 'Tcl File Updater'. -- Sarnold
Usage:
tfu ?--debug boolean? ?--repare boolean? /src/dir/ /target/dir/
Output:
U /updated/file C /created/directory/ R /restored/file A /file/added/to/target/dir
Notes
I use this script daily for the sake of not to use any tool like CVS for small development. I think it is close to rsync, with the restriction that it will not operate with distant machines like rsync does.
The script
#!/usr/bin/env tclsh
set cwd [pwd]
array set opts [list debug off repare off]
proc testopt {key val} {
global opts
foreach {opt oname} {-d debug -r repare} {
if {$key eq $opt || $key eq "--$oname"} {
set opts($oname) $val
return true
}
}
return false
}
proc usage {} {
puts {usage: tfu [options] sourcedir/ targetdir/
Options are:
-d --debug boolean debug mode (does not alter the filesystem)
-h --help prints this message}
exit 0
}
if {[llength $argv]<2} {
usage
}
foreach {source target} [lrange $argv end-1 end] break
foreach {key val} [lrange $argv 0 end-2] {
if {[testopt $key $val]} continue
error "unknown option $key"
}
proc Repare {dir with} {
cd $with
if {![file exists $dir]} {
puts "C $dir"
Mkdir $dir
}
set dirs [list]
foreach f [glob -nocomplain *] {
if {[file isdirectory $f]} {
lappend dirs $f
} elseif {[file isfile $f] && ![file exists $dir/$f]} {
puts "R $dir/$f"
Copy $f $dir/$f
}
}
foreach d $dirs {
Repare $dir/$d $with/$d
}
}
proc Update {dir with} {
cd $with
if {![file exists $dir]} {
puts "C $dir"
Mkdir $dir
}
set dirs [list]
foreach f [glob -nocomplain *] {
if {[file isdirectory $f]} {
lappend dirs $f
} elseif {[file isfile $f]} {
if {![file exists $dir/$f]} {
puts "A $dir/$f"
Copy $f $dir/$f
} elseif {[file mtime $dir/$f] < [file mtime $f]} {
puts "U $dir/$f"
Copy-force $f $dir/$f
}
}
}
foreach d $dirs {
Update $dir/$d $with/$d
}
}
proc CreateShadowProcs {} {
global opts
if {!$opts(debug)} {
interp alias {} Cd {} cd
interp alias {} Copy {} file copy
interp alias {} Copy-force {} file copy -force
interp alias {} Mkdir {} file mkdir
} else {
proc Cd {dir} {catch {cd $dir}}
proc Copy {a b} {catch {file copy $a $b}}
proc Copy-force {a b} {catch {file copy -force $a $b}}
proc Mkdir {d} {catch {file mkdir $d}}
}
}
proc main {args} {
global source target opts
set source [checkDir $source]
set target [checkDir $target]
CreateShadowProcs
if {$opts(repare)} {
Repare $source $target
}
Update $target $source
}
proc checkDir {name} {
if {![file isdirectory $name]} {error "not a valid directory: $name"}
file normalize $name
}
catch {tcldebug::debug}
main