123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- package main
- import (
- "fmt"
- "io"
- "os"
- "os/signal"
- "path/filepath"
- "regexp"
- "strings"
- "syscall"
- "time"
- "git.wecise.com/wecise/util/ssh"
- "github.com/spf13/cast"
- ccfg "github.com/wecisecode/util/cfg"
- "github.com/wecisecode/util/filewalker"
- clog "github.com/wecisecode/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 <frompath> <topath> [<copytopath> ...]
- `)
- }
- var option = struct {
- Debug bool
- }{
- Debug: false,
- }
- func main() {
- var frompath string = ""
- var topath string = ""
- var copytopath = []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
- } else {
- copytopath = append(copytopath, arg)
- }
- }
- }
- switch {
- case frompath != "" && topath != "":
- err := sync(frompath, topath, copytopath...)
- if err != nil {
- println(err.Error())
- os.Exit(1)
- }
- default:
- PrintUsage()
- os.Exit(1)
- }
- }
- var REremotepath = regexp.MustCompile(`^([^:]+):([^@]+)@([^/]+)(/.*)`)
- var REabspath = regexp.MustCompile(`^((?:/.*)|(?:[a-zA-Z]:\.*)|(?:[a-zA-Z]:))`)
- func sync(frompath, topath string, copytopath ...string) (err error) {
- frompath, e := filepath.Abs(frompath)
- if e != nil {
- err = e
- return
- }
- topath, e = filepath.Abs(topath)
- if e != nil {
- err = e
- return
- }
- fmt.Println("sync from", frompath, "to", topath, "\ncopy to", copytopath)
- for {
- err = sync_once(frompath, topath, copytopath...)
- if err != nil {
- return
- }
- time.Sleep(5 * time.Second)
- }
- }
- func sync_once(frompath, topath string, copytopath ...string) (err error) {
- 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
- }
- hiddenpath := fmt.Sprintf(`.*\%c\..*`, os.PathSeparator)
- if regexp.MustCompile(`^[^\.].*`).MatchString(fpath) && !regexp.MustCompile(hiddenpath).MatchString(fpath) {
- fromfile := filepath.Join(absbasedir, fpath)
- tofile := filepath.Join(topath, fpath)
- if option.Debug {
- fmt.Println(time.Now().Format("2006-01-02 15:04:05"), fromfile, " ==> ", tofile)
- }
- 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
- }
- WriteFile(topath, fpath, frombs, fromfi.ModTime())
- for _, ctpath := range copytopath {
- WriteFile(ctpath, fpath, frombs, fromfi.ModTime())
- }
- 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 filesize int64
- var bufsize int
- if info, err := f.Stat(); err == nil {
- filesize = info.Size()
- if int64(int(filesize)) == filesize {
- bufsize = int(filesize)
- }
- }
- bufsize++ // 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 := int64(len(data))
- if bufsize > cap(data) {
- ndata := make([]byte, len(data), bufsize)
- copy(ndata, data)
- data = ndata
- }
- statuschan := make(chan int64, 100)
- go func() {
- for range statuschan {
- s := fmt.Sprintf("%.2f%%", 100*float64(offset)/float64(filesize))
- bs := strings.Repeat("\b", len(s))
- fmt.Printf("%s%s", bs, s)
- }
- if offset >= filesize {
- s := fmt.Sprintf("%.2f%%", 100.)
- bs := strings.Repeat("\b", len(s))
- fmt.Printf("%s%s", bs, s)
- }
- }()
- chunk := int64(1024 * 256)
- t := time.Now()
- for {
- to := offset + chunk
- if to > int64(cap(data)) {
- to = int64(cap(data))
- }
- n, err := f.ReadAt(data[offset:to], int64(offset))
- offset += int64(n)
- statuschan <- offset
- if err != nil {
- close(statuschan)
- return data[:offset], err
- }
- if offset >= int64(cap(data)) {
- d := append(data[:cap(data)], 0)
- data = d[:offset]
- }
- ut := time.Since(t)
- if ut < 500*time.Millisecond {
- chunk *= 2
- } else if chunk > 1024 && ut > 5*time.Second {
- chunk /= 2
- }
- t = time.Now()
- }
- }
- func WriteFile(basedir string, fpath string, bs []byte, mtime time.Time) (err error) {
- ss := REremotepath.FindStringSubmatch(basedir)
- if len(ss) == 5 {
- username, password, hostport, remotepath := ss[1], ss[2], ss[3], regexp.MustCompile("//+").ReplaceAllString(ss[4], "/")
- return WriteRemoteFile(username, password, hostport, remotepath, fpath, bs, mtime)
- }
- tofile := filepath.Join(basedir, fpath)
- todir := filepath.Dir(tofile)
- e := os.MkdirAll(todir, os.ModePerm)
- if e != nil {
- err = fmt.Errorf("to dir %v", e)
- return
- }
- e = os.WriteFile(tofile, bs, os.ModePerm)
- if e != nil {
- err = fmt.Errorf("to file %v", e)
- return
- }
- e = os.Chtimes(tofile, mtime, mtime)
- if e != nil {
- err = fmt.Errorf("to file %v", e)
- return
- }
- return nil
- }
- func WriteRemoteFile(username, password, hostport, remotepath, fpath string, bs []byte, mtime time.Time) error {
- fpath = strings.ReplaceAll(fpath, "\\", "/")
- fmt.Println("copy to:", fmt.Sprint(username, ":", password, "@", hostport, "/", remotepath, "/", fpath))
- hp := strings.Split(hostport, ":")
- host := hp[0]
- port := 22
- if len(hp) >= 2 && hp[1] != "" {
- port = cast.ToInt(hp[1])
- }
- node := &ssh.Node{
- User: username,
- Password: password,
- Host: host,
- Port: port,
- }
- client, e := node.Connect()
- if e != nil {
- return e
- }
- rfpath := remotepath + "/" + fpath
- diridx := strings.LastIndex(rfpath, "/")
- e = client.MkdirAll(rfpath[:diridx])
- if e != nil {
- return e
- }
- e = client.WriteFile(rfpath, bs, os.ModePerm, mtime)
- if e != nil {
- return e
- }
- return nil
- }
|