package main import ( "fmt" "io" "os" "os/signal" "path/filepath" "regexp" "strings" "syscall" "time" ccfg "git.wecise.com/wecise/util/cfg" "git.wecise.com/wecise/util/filewalker" clog "git.wecise.com/wecise/util/logger" ) func init() { exitChan := make(chan os.Signal, 1) signal.Notify(exitChan, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGABRT, syscall.SIGTERM) go func() { <-exitChan os.Exit(1) }() } var config = ccfg.MConfig() var logger = clog.New() // 同步指定文件夹中的内容 func PrintUsage() { println(`Usage: sync `) } var option = struct { Debug bool }{ Debug: false, } func main() { var frompath string = "" var topath string = "" for _, arg := range os.Args[1:] { if strings.HasPrefix(arg, "-") { switch arg { case "-debug": option.Debug = true } } else if arg != "" { if frompath == "" { frompath = arg } else if topath == "" { topath = arg } } } switch { case frompath != "" && topath != "": fmt.Println("sync from", frompath, "to", topath) for { err := sync(frompath, topath) if err != nil { println(err.Error()) os.Exit(1) } time.Sleep(5 * time.Second) } default: PrintUsage() os.Exit(1) } } func sync(frompath, topath string) (err error) { if len(frompath) == 0 || !(len(frompath) >= 1 && frompath[:1] == "/") && !(len(frompath) >= 2 && frompath[1:2] == ":") && !regexp.MustCompile(`^[^:]+:[^@]+@[^/]+/.*`).MatchString(frompath) { return fmt.Errorf("Must specify absolute path") } if len(topath) == 0 || !(len(topath) >= 1 && topath[:1] == "/") && !(len(topath) >= 2 && topath[1:2] == ":") && !regexp.MustCompile(`^[^:]+:[^@]+@[^/]+/.*`).MatchString(topath) { return fmt.Errorf("Must specify absolute path") } fw, e := filewalker.NewFileWalker([]string{frompath}, `^[^\.].*`) // orderby: dirfirst, filefirst, fullpath if e != nil { return e } count := 0 e = fw.List(func(basedir, fpath string) bool { if option.Debug { fmt.Println("basedir=", basedir, ", fpath=", fpath) } absbasedir, e := filepath.Abs(basedir) if e != nil { err = e return false } abstopath, e := filepath.Abs(topath) if e != nil { err = e return false } hiddenpath := fmt.Sprintf(`.*\%c\..*`, os.PathSeparator) if regexp.MustCompile(`^[^\.].*`).MatchString(fpath) && !regexp.MustCompile(hiddenpath).MatchString(fpath) { fromfile := filepath.Join(absbasedir, fpath) tofile := filepath.Join(abstopath, fpath) if option.Debug { fmt.Println(time.Now().Format("2006-01-02 15:04:05"), fromfile, " ==> ", tofile) } todir := filepath.Dir(tofile) e = os.MkdirAll(todir, os.ModePerm) if e != nil { err = fmt.Errorf("to dir %v", e) return false } fromfi, e := os.Stat(fromfile) if e != nil { err = fmt.Errorf("from file %v", e) return false } tofi, e := os.Stat(tofile) if e != nil && !os.IsNotExist(e) { err = fmt.Errorf("to file %v", e) return false } if tofi != nil && fromfi.Size() == tofi.Size() && fromfi.ModTime() == tofi.ModTime() { return true } fmt.Print(fromfile, " ==> ", tofile) frombs, e := ReadFile(fromfile) if e != nil { err = fmt.Errorf("from file %v", e) return false } e = os.WriteFile(tofile, frombs, os.ModePerm) if e != nil { err = fmt.Errorf("to file %v", e) return false } e = os.Chtimes(tofile, fromfi.ModTime(), fromfi.ModTime()) if e != nil { err = fmt.Errorf("to file %v", e) return false } count++ } return true }) if e != nil { return e } if err != nil { return } if count > 0 || option.Debug { fmt.Println(time.Now().Format("2006-01-02 15:04:05"), count, "files copied") } return } func ReadFile(name string) (data []byte, err error) { fmt.Print(" -.---") defer fmt.Println() data = make([]byte, 0, 512) for { data, err = ReadFileContinue(name, data) if err == io.EOF { err = nil return } } } func ReadFileContinue(name string, data []byte) ([]byte, error) { f, err := os.Open(name) if err != nil { return data, err } defer f.Close() var size int if info, err := f.Stat(); err == nil { size64 := info.Size() if int64(int(size64)) == size64 { size = int(size64) } } size++ // one byte for final read at EOF // If a file claims a small size, read at least 512 bytes. // In particular, files in Linux's /proc claim size 0 but // then do not work right if read in small pieces, // so an initial read of 1 byte would not work correctly. offset := len(data) if size > cap(data) { ndata := make([]byte, len(data), size) copy(ndata, data) data = ndata } statuschan := make(chan int, 100) go func() { for range statuschan { s := fmt.Sprintf("%1.3f", float64(offset)/float64(size)) bs := strings.Repeat("\b", len(s)) fmt.Printf("%s%s", bs, s) } }() for { to := offset + 1024*512 if to > cap(data) { to = cap(data) } n, err := f.ReadAt(data[offset:to], int64(offset)) offset += n statuschan <- offset if err != nil { close(statuschan) return data[:offset], err } if offset >= cap(data) { d := append(data[:cap(data)], 0) data = d[:offset] } } }