前回、同一のパソコン間で、
あるディレクトリをまるまるバックアップするスクリプトを書いた。
その時、
ディレクトリをWalkという関数で再帰的に調べてディレクトリやファイルをコピーした。
前回のコードではファイルのコピーは必ず行うという無駄な処理が多いため、
今回はファイルの更新日時を調べて、バックアップ元の方が新しければコピーを行う
という処理を追加してみることにした。
Walkで再帰的に調べて、ファイルがあった場合、
infoという変数(FIleInfo構造体)に元データのファイル情報が入っているとする。
FileInfoの構造は下記の通り
type FileInfo interface { Name() string Size() int64 Mode() FileMode ModTime() time.Time IsDir() bool Sys() interface{} }
type FileInfo | os - The Go Programming Language
ModTime()で更新日が格納されているTime型で取れることがわかった。
ということで、
smod := info.ModTime() ssec := smod.Unix()
※smodはsrcのmodの略
これで、元データの更新時刻のUnixタイムを取得できた。
あと必要なのが、
バックアップ先にあるファイルの更新時刻だけど、
今あるファイルのFileInfoを取得しなければならない。
FileInfoはファイルパスを指定してOpenすれば取得できるので、
dst, err = os.Open(backupDir + fpath) if err == nil { finfo, _ := dst.Stat() fmod := finfo.ModTime() dsec := fmod.Unix() }
これで良い。
元データの方と合わせて、
//doCopyがtrueであればファイルのコピーを行う doCopy = false //すでにコピーがある場合はファイルの更新日を調べて、コピーするか決める dst, err = os.Open(backupDir + fpath) if err == nil { finfo, _ := dst.Stat() fmod := finfo.ModTime() dsec := fmod.Unix() //コピー元の更新日を調べる smod := info.ModTime() ssec := smod.Unix() //更新日を比較して、元データの方が新しければtrue if dsec < ssec { doCopy = true } } else { //バックアップ先にファイルがなければ常にtrue doCopy = true } dst.Close()
これでコピーを行うかどうかの判定ができました。
今回の内容を加味したコードが下記になります。
package main import ( "fmt" "io" "log" "os" "path/filepath" "strings" ) func main() { var dir string var fpath string var src *os.File var dst *os.File srcdir := "/home/saito/goraw/" current, _ := filepath.Abs(".") backupDir := current + "/backup/" //バックアップ用ディレクトリがなければ作成 if _, err := os.Stat(backupDir); err != nil { if err = os.Mkdir(backupDir, 0777); err != nil { log.Println(err) } } err := filepath.Walk(srcdir, func(path string, info os.FileInfo, err error) error { if err != nil { log.Println(err) } //ディレクトリかどうかを調べる if info.IsDir() { fmt.Println(path + "はディレクトリです") //ディレクトリの場合は、バックアップの方にディレクトリがあるか調べて、なければ作る dir = strings.Replace(path, srcdir, "", 1) if _, err = os.Stat(backupDir + dir); err != nil { fmt.Println(backupDir + dir + "ディレクトリを作成します") err = os.Mkdir(backupDir+dir, 0777) if err != nil { log.Println(err) } } } else { //互いのファイルの更新日を調べて、コピーをするか決める doCopy := false fmt.Println(path + "はファイルです") fpath = strings.Replace(path, srcdir, "", 1) //すでにコピーがある場合はファイルの更新日を調べて、コピーするか決める dst, err = os.Open(backupDir + fpath) if err == nil { finfo, _ := dst.Stat() fmod := finfo.ModTime() dsec := fmod.Unix() //コピー元の更新日を調べる smod := info.ModTime() ssec := smod.Unix() if dsec < ssec { doCopy = true } } else { //バックアップ先にファイルがなければ常にtrue doCopy = true } dst.Close() //サーバにあるファイルの更新日が新しい場合はコピーを実行 if doCopy { fmt.Println(info.Name() + "をコピーします") src, err = os.Open(path) if err != nil { log.Println(err) } dst, err = os.Create(backupDir + fpath) if err != nil { log.Println(err) } if _, err = io.Copy(dst, src); err != nil { log.Println(err) } src.Close() dst.Close() } } return nil }) if err != nil { log.Println(err) } }
一部冗長していますが、頭の整理を兼ねてなのでご愛嬌ください。
あとはSSHを挟んだ処理を追加するってところかな。