sync.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "os/signal"
  7. "path/filepath"
  8. "regexp"
  9. "strings"
  10. "syscall"
  11. "time"
  12. ccfg "git.wecise.com/wecise/util/cfg"
  13. "git.wecise.com/wecise/util/filewalker"
  14. clog "git.wecise.com/wecise/util/logger"
  15. )
  16. func init() {
  17. exitChan := make(chan os.Signal, 1)
  18. signal.Notify(exitChan, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGABRT, syscall.SIGTERM)
  19. go func() {
  20. <-exitChan
  21. os.Exit(1)
  22. }()
  23. }
  24. var config = ccfg.MConfig()
  25. var logger = clog.New()
  26. // 同步指定文件夹中的内容
  27. func PrintUsage() {
  28. println(`Usage:
  29. sync <frompath> <topath>
  30. `)
  31. }
  32. var option = struct {
  33. Debug bool
  34. }{
  35. Debug: false,
  36. }
  37. func main() {
  38. var frompath string = ""
  39. var topath string = ""
  40. for _, arg := range os.Args[1:] {
  41. if strings.HasPrefix(arg, "-") {
  42. switch arg {
  43. case "-debug":
  44. option.Debug = true
  45. }
  46. } else if arg != "" {
  47. if frompath == "" {
  48. frompath = arg
  49. } else if topath == "" {
  50. topath = arg
  51. }
  52. }
  53. }
  54. switch {
  55. case frompath != "" && topath != "":
  56. fmt.Println("sync from", frompath, "to", topath)
  57. for {
  58. err := sync(frompath, topath)
  59. if err != nil {
  60. println(err.Error())
  61. os.Exit(1)
  62. }
  63. time.Sleep(5 * time.Second)
  64. }
  65. default:
  66. PrintUsage()
  67. os.Exit(1)
  68. }
  69. }
  70. func sync(frompath, topath string) (err error) {
  71. if len(frompath) == 0 ||
  72. !(len(frompath) >= 1 && frompath[:1] == "/") &&
  73. !(len(frompath) >= 2 && frompath[1:2] == ":") &&
  74. !regexp.MustCompile(`^[^:]+:[^@]+@[^/]+/.*`).MatchString(frompath) {
  75. return fmt.Errorf("Must specify absolute path")
  76. }
  77. if len(topath) == 0 ||
  78. !(len(topath) >= 1 && topath[:1] == "/") &&
  79. !(len(topath) >= 2 && topath[1:2] == ":") &&
  80. !regexp.MustCompile(`^[^:]+:[^@]+@[^/]+/.*`).MatchString(topath) {
  81. return fmt.Errorf("Must specify absolute path")
  82. }
  83. fw, e := filewalker.NewFileWalker([]string{frompath}, `^[^\.].*`) // orderby: dirfirst, filefirst, fullpath
  84. if e != nil {
  85. return e
  86. }
  87. count := 0
  88. e = fw.List(func(basedir, fpath string) bool {
  89. if option.Debug {
  90. fmt.Println("basedir=", basedir, ", fpath=", fpath)
  91. }
  92. absbasedir, e := filepath.Abs(basedir)
  93. if e != nil {
  94. err = e
  95. return false
  96. }
  97. abstopath, e := filepath.Abs(topath)
  98. if e != nil {
  99. err = e
  100. return false
  101. }
  102. hiddenpath := fmt.Sprintf(`.*\%c\..*`, os.PathSeparator)
  103. if regexp.MustCompile(`^[^\.].*`).MatchString(fpath) && !regexp.MustCompile(hiddenpath).MatchString(fpath) {
  104. fromfile := filepath.Join(absbasedir, fpath)
  105. tofile := filepath.Join(abstopath, fpath)
  106. if option.Debug {
  107. fmt.Println(time.Now().Format("2006-01-02 15:04:05"), fromfile, " ==> ", tofile)
  108. }
  109. todir := filepath.Dir(tofile)
  110. e = os.MkdirAll(todir, os.ModePerm)
  111. if e != nil {
  112. err = fmt.Errorf("to dir %v", e)
  113. return false
  114. }
  115. fromfi, e := os.Stat(fromfile)
  116. if e != nil {
  117. err = fmt.Errorf("from file %v", e)
  118. return false
  119. }
  120. tofi, e := os.Stat(tofile)
  121. if e != nil && !os.IsNotExist(e) {
  122. err = fmt.Errorf("to file %v", e)
  123. return false
  124. }
  125. if tofi != nil && fromfi.Size() == tofi.Size() && fromfi.ModTime() == tofi.ModTime() {
  126. return true
  127. }
  128. fmt.Print(fromfile, " ==> ", tofile)
  129. frombs, e := ReadFile(fromfile)
  130. if e != nil {
  131. err = fmt.Errorf("from file %v", e)
  132. return false
  133. }
  134. e = os.WriteFile(tofile, frombs, os.ModePerm)
  135. if e != nil {
  136. err = fmt.Errorf("to file %v", e)
  137. return false
  138. }
  139. e = os.Chtimes(tofile, fromfi.ModTime(), fromfi.ModTime())
  140. if e != nil {
  141. err = fmt.Errorf("to file %v", e)
  142. return false
  143. }
  144. count++
  145. }
  146. return true
  147. })
  148. if e != nil {
  149. return e
  150. }
  151. if err != nil {
  152. return
  153. }
  154. if count > 0 || option.Debug {
  155. fmt.Println(time.Now().Format("2006-01-02 15:04:05"), count, "files copied")
  156. }
  157. return
  158. }
  159. func ReadFile(name string) (data []byte, err error) {
  160. fmt.Print(" -.--%")
  161. defer fmt.Println()
  162. data = make([]byte, 0, 512)
  163. for {
  164. data, err = ReadFileContinue(name, data)
  165. if err == io.EOF {
  166. err = nil
  167. return
  168. }
  169. }
  170. }
  171. func ReadFileContinue(name string, data []byte) ([]byte, error) {
  172. f, err := os.Open(name)
  173. if err != nil {
  174. return data, err
  175. }
  176. defer f.Close()
  177. var filesize int64
  178. var bufsize int
  179. if info, err := f.Stat(); err == nil {
  180. filesize = info.Size()
  181. if int64(int(filesize)) == filesize {
  182. bufsize = int(filesize)
  183. }
  184. }
  185. bufsize++ // one byte for final read at EOF
  186. // If a file claims a small size, read at least 512 bytes.
  187. // In particular, files in Linux's /proc claim size 0 but
  188. // then do not work right if read in small pieces,
  189. // so an initial read of 1 byte would not work correctly.
  190. offset := int64(len(data))
  191. if bufsize > cap(data) {
  192. ndata := make([]byte, len(data), bufsize)
  193. copy(ndata, data)
  194. data = ndata
  195. }
  196. statuschan := make(chan int64, 100)
  197. go func() {
  198. for range statuschan {
  199. s := fmt.Sprintf("%.2f%%", 100*float64(offset)/float64(filesize))
  200. bs := strings.Repeat("\b", len(s))
  201. fmt.Printf("%s%s", bs, s)
  202. }
  203. if offset >= filesize {
  204. s := fmt.Sprintf("%.2f%%", 100.)
  205. bs := strings.Repeat("\b", len(s))
  206. fmt.Printf("%s%s", bs, s)
  207. }
  208. }()
  209. chunk := int64(1024 * 256)
  210. t := time.Now()
  211. for {
  212. to := offset + chunk
  213. if to > int64(cap(data)) {
  214. to = int64(cap(data))
  215. }
  216. n, err := f.ReadAt(data[offset:to], int64(offset))
  217. offset += int64(n)
  218. statuschan <- offset
  219. if err != nil {
  220. close(statuschan)
  221. return data[:offset], err
  222. }
  223. if offset >= int64(cap(data)) {
  224. d := append(data[:cap(data)], 0)
  225. data = d[:offset]
  226. }
  227. ut := time.Since(t)
  228. if ut < 500*time.Millisecond {
  229. chunk *= 2
  230. } else if chunk > 1024 && ut > 5*time.Second {
  231. chunk /= 2
  232. }
  233. t = time.Now()
  234. }
  235. }