Go1.7.3运维文件夹比较工具,可以支持远程比较

package main

import (
    "bufio"
    "bytes"
    "crypto/md5"
    "encoding/hex"
    "errors"
    "flag"
    "fmt"
    "hash"
    "io"
    "log"
    "net/http"
    "os"
    "path/filepath"
    "strings"
)

type compareConfig struct {
    dpath   string
    spath   string
    quick   bool
    diff    string
    output  string
    split   string
    getmd5  bool
    md5list string
    laddr   string
}

var compareCFG compareConfig

func init() {
    flag.StringVar(&compareCFG.spath, "s", "", `-s="server" 指定源目录或文件`)
    flag.StringVar(&compareCFG.dpath, "d", "", `-d="server_back" 指定目标目录或文件`)
    flag.StringVar(&compareCFG.diff, "c", "", `-c="diff" 把源目录不匹配的文件提出来,为空则不拷贝`)
    flag.StringVar(&compareCFG.output, "o", "", `指定匹配的结果输出文件,不指定则输出到标准输出`)
    flag.StringVar(&compareCFG.split, "S", "    ", `-S=" " 验证指定路径文件的md5值的时候指定md5和路径的分隔符`)
    flag.StringVar(&compareCFG.md5list, "F", "", `-F="md5list" 验证指定路径文件的md5值,每行一条数据,md5码和文件路径,结合-d使用`)
    flag.BoolVar(&compareCFG.quick, "q", true, `-q=false 快速模式即用文件内容判断文件是否一致,如果为false则使用md5比较`)
    flag.BoolVar(&compareCFG.getmd5, "m", false, "-m true 获取指定目录下所有文件的md5,结合-s指定目录使用")
    flag.StringVar(&compareCFG.laddr, "l", "", `-l="192.168.1.2:80" 通过http协议共享md5列表,结合-m=true使用`)
    flag.Parse()
}

func main() {
    if len(os.Args) <= 1 {
        flag.PrintDefaults()
        return
    }
    var w = os.Stdout
    if compareCFG.output != "" {
        var err error
        w, err = os.Create(compareCFG.output)
        if err != nil {
            log.Println(err)
            return
        }
    }

    if compareCFG.getmd5 {
        if compareCFG.spath != "" {
            if compareCFG.laddr != "" {
                err := server(compareCFG.laddr, compareCFG.spath)
                if err != nil {
                    log.Println(err)
                }
                return
            }
            err := GetMd5(compareCFG.spath, w)
            if err != nil {
                log.Println(err)
            }
        } else {
            log.Println("必须指定目录路径")
        }
        return
    }

    if compareCFG.spath != "" && compareCFG.dpath != "" {
        comparePath(compareCFG.spath, compareCFG.dpath, compareCFG.diff, w, compareCFG.quick)
        return
    }

    if compareCFG.md5list != "" && compareCFG.dpath != "" {
        var r io.ReadCloser
        var err error
        defer func() {
            if r != nil {
                r.Close()
            }
        }()

        if strings.HasPrefix(compareCFG.md5list, "http") {
            //  client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
            resp, err := http.Get(compareCFG.md5list)
            if err != nil {
                log.Println(err)
                return
            }
            r = resp.Body
        } else {
            r, err = os.Open(compareCFG.md5list)
            if err != nil {
                log.Println(err)
                return
            }
        }
        compareFromFile(r, compareCFG.dpath, compareCFG.split, w)
        return
    }
    flag.PrintDefaults()
}

func compareFromFile(File io.Reader, dir, split string, w io.Writer) {
    dir = formatSeparator(dir)
    buf := bufio.NewReader(File)
    h := md5.New()
    var m, dfile, md string
    var count int = 0
    for {
        count += 1
        line, _, err := buf.ReadLine()
        if err != nil {
            if err != io.EOF {
                log.Println(err)
            }
            break
        }

        list := bytes.Split(line, []byte(split))
        if len(list) != 2 {
            log.Printf("第%d行数据无效,确认分隔符\n", count)
            continue
        }
        dfile = string(bytes.TrimSpace(list[1]))
        md, err = getmd5(h, dir+dfile)
        if err != nil {
            fmt.Fprintf(w, "文件不存在:\t%s\n", dfile)
            continue
        }

        m = string(bytes.TrimSpace(list[0]))
        if md != m {
            fmt.Fprintf(w, "内容不一致:\t%s\n", dfile)
        }
    }
}

func comparePath(spath, dpath, diff string, w io.Writer, quick bool) {
    sinfo, err := os.Lstat(spath)
    if err != nil {
        log.Println(err)
        return
    }
    dinfo, err := os.Lstat(dpath)
    if err != nil {
        log.Println(err)
        return
    }

    if !sinfo.IsDir() && !dinfo.IsDir() {
        if comparefile(spath, dpath, quick) {
            fmt.Fprintf(w, "内容一致:\t%s\n", dinfo.Name())
        } else {
            fmt.Fprintf(w, "内容不一致:\t%s\n", dinfo.Name())
        }
        return
    }

    if !(sinfo.IsDir() || dinfo.IsDir()) {
        log.Println("原路径和目标路径必须同时为文件或者同时为目录")
        return
    }

    if diff != "" {
        diff = formatSeparator(diff)
        os.RemoveAll(diff)
        os.MkdirAll(diff, 0666)
    }

    spath = formatSeparator(spath)
    dpath = formatSeparator(dpath)

    filepath.Walk(spath, func(root string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if info.IsDir() {
            return nil
        }

        path := strings.TrimPrefix(root, spath)
        dfile := dpath + path
        dinfo, err := os.Lstat(dfile)
        if err != nil {
            if os.IsNotExist(err) {
                fmt.Fprintf(w, "文件不存在:\t%s\n", path)
                if diff != "" {
                    err = copyFile(root, diff+path)
                    if err != nil {
                        log.Printf("拷贝文件错误:%s\n", err)
                    } else {
                        log.Printf("拷贝文件成功:%s\n", path)
                    }
                }
            }
            return nil
        }

        if info.Size() == dinfo.Size() {
            if comparefile(root, dfile, quick) {
                fmt.Fprintf(w, "内容一致:\t%s\n", path)
                return nil
            }
        }
        fmt.Fprintf(w, "内容不一致:\t%s\n", path)
        if diff != "" {
            err = copyFile(root, diff+path)
            if err != nil {
                log.Printf("拷贝文件错误:%s\n", err)
            } else {
                log.Printf("拷贝文件成功:%s\n", path)
            }
        }
        return nil
    })
    return
}

func comparefile(spath, dpath string, quick bool) bool {
    if !quick {
        h := md5.New()
        smd5, err := getmd5(h, spath)
        if err != nil {
            return false
        }
        dmd5, err := getmd5(h, dpath)
        if err != nil {
            return false
        }
        return smd5 == dmd5
    }

    sFile, err := os.Open(spath)
    if err != nil {
        return false
    }
    defer sFile.Close()
    dFile, err := os.Open(dpath)
    if err != nil {
        return false
    }
    defer dFile.Close()
    return comparebyte(sFile, dFile)
}

//下面可以代替md5比较.
func comparebyte(sfile io.Reader, dfile io.Reader) bool {
    var sbyte []byte = make([]byte, 512)
    var dbyte []byte = make([]byte, 512)
    var serr, derr error
    for {
        _, serr = sfile.Read(sbyte)
        _, derr = dfile.Read(dbyte)
        if serr != nil || derr != nil {
            if serr != derr {
                return false
            }
            if serr == io.EOF {
                break
            }
        }
        if bytes.Equal(sbyte, dbyte) {
            continue
        }
        return false
    }
    return true
}

func copyFile(spath, dpath string) error {
    err := copyfile(spath, dpath)
    if err != nil {
        return err
    }
    info, err := os.Lstat(spath)
    if err != nil {
        return err
    }
    os.Chmod(dpath, info.Mode())
    os.Chtimes(dpath, info.ModTime(), info.ModTime())
    return nil
}

func copyfile(spath, dpath string) error {
    basedir := filepath.Dir(dpath)
    err := os.MkdirAll(basedir, 0666)
    if err != nil {
        return err
    }
    dFile, err := os.Create(dpath)
    if err != nil {
        return err
    }
    defer dFile.Close()
    sFile, err := os.Open(spath)
    if err != nil {
        return err
    }
    defer sFile.Close()
    _, err = io.Copy(dFile, sFile)
    return err
}

func formatSeparator(path string) string {
    Separator := string(filepath.Separator)
    if Separator != "/" {
        path = strings.Replace(path, "/", Separator, -1)
    } else {
        path = strings.Replace(path, "\\", Separator, -1)
    }
    if !strings.HasSuffix(path, Separator) {
        path += Separator
    }
    return path
}

func GetMd5(path string, w io.Writer) error {
    path = filepath.Clean(path)
    info, err := os.Lstat(path)
    if err != nil {
        return err
    }
    if !info.IsDir() {
        return errors.New("路径必须是目录")
    }
    if !strings.HasSuffix(path, string(filepath.Separator)) {
        path += string(filepath.Separator)
    }
    h := md5.New()
    return filepath.Walk(path, func(root string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if info.IsDir() {
            return nil
        }
        dir := strings.TrimPrefix(root, path)
        m, err := getmd5(h, root)
        if err != nil {
            log.Println("计算md5失败:", err.Error())
            return nil
        }
        if filepath.Separator == '\\' {
            dir = strings.Replace(dir, "\\", "/", -1)
        }

        fmt.Fprintf(w, "%s\t%s\n", m, dir)
        return nil
    })
}

func getmd5(md5hash hash.Hash, path string) (string, error) {
    File, err := os.Open(path)
    if err != nil {
        return "", err
    }
    defer File.Close()
    md5hash.Reset()
    _, err = io.Copy(md5hash, File)
    if err != nil {
        return "", err
    }
    result := make([]byte, 0, 32)
    result = md5hash.Sum(result)
    return hex.EncodeToString(result), nil
}

func server(laddr string, dirpath string) error {
    var list []byte
    flush := func() error {
        buf := bytes.NewBuffer(nil)
        if err := GetMd5(dirpath, buf); err != nil {
            return err
        }
        list = buf.Bytes()
        return nil
    }

    if err := flush(); err != nil {
        return err
    }

    route := func(w http.ResponseWriter, r *http.Request) {
        log.Printf("远程地址:%s 访问路径:%s\n", r.RemoteAddr, r.URL.Path)
        defer r.Body.Close()
        switch r.URL.Path {
        default:
            fmt.Fprintln(w, `<a href="/getmd5">查看MD5列表</a><br><a href="/flush">刷新MD5列表</a>`)
        case "/getmd5":
            fmt.Fprintf(w, "%s", list)
        case "/flush":
            if err := flush(); err != nil {
                fmt.Fprintf(w, "刷新md5列表失败,%s\n", err)
            } else {
                fmt.Fprintln(w, "刷新md5列表成功")
            }
        }
    }

    http.HandleFunc("/", route)
    return http.ListenAndServe(laddr, nil)
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ExamDiff Pro 是一个可视化的文件和目录比较工具,它具有一系列简单方便的比较功能 (可比 Beyond Compare 方便好用多了!)。 - 比较文本文件,二进制文件和目录。 - 高亮不同差异 (深入到行、词、字符级别)。 - 允许在文件比较窗格内编辑文件。 - 打印和打印预览差异报告。 - 完全支持 UNICODE。 - 允许为以后的比较创建目录快照。 - 执行同步和自动换行。 - 用户可以指定记住第一个和第二个文件或目录的数量。 - 不需要制定两个文件名,仅输入一个目录和一个文件名进行比较,EximDiff 将会尝试使用先前指定目录下的文件名进行比较。 - 在第一个和第二个文件或目录之间进行切换。 - 自动检测文件或目录变化,并且提示用户重新比较。 - 使用重新比较功能后,视图焦点将会保持与重新比较之前保持一致。 - 支持拖放操作,可以从 Windows 资源管理器中拖动两个文件或目录进行比较。 - 在目录比较窗格内,只要简单地双击文件或目录,就可以执行比较。 - 在目录比较窗格内,通过菜单、工具栏按钮或右键弹出菜单,可以复制、重命名和删除文件或目录。这其中的任何操作都会自动地重新同步比较的目录。你还可以在左边或右边的窗格内,按名称、大小、类型或最后修改时间,对目录进行排序。 - 易用的文件编辑功能。 ExamDiff Pro 还可以给定文件名和插入符位置,调用任何的外部编辑器 (用户配置)。 - 保存差异文件 (标准 UNIX 差异文件) 或 HTML 差异文件。 - 通过“下一个差异”和“上一个差异”按钮和热键,或所有差异列表框,用户可以在差异之间方便地浏览。 - 在文件比较窗格内,可以通过拖放、热键或右键菜单来复制文本。 - 可以通过简单的“搜索” 命令在比较窗格内搜索字符串。另外,ExamDiff Pro 将会记住用户指定数量的新近搜索。 - 可定制颜色。 - 完全可订制的文件扩展名过滤器。例如,用户可以选择只显示 .c 和 .cpp 文件,或任何其它的文件。 - 完善的工具栏提示支持。 - 可调整的窗格分割栏,具有平滑同步滚动功能。 - 可以在水平分割和垂直分割之间任意切换,或者隐藏窗格。 - 命令行支持: 用法: ExamDiff [名称1] [名称2] [选项] 或 ExamDiff /se:会话 [名称1] [名称2]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值