-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscan.go
134 lines (120 loc) · 2.83 KB
/
scan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"syscall"
)
// FileInfo 包含文件路径和其哈希值
type FileInfo struct {
Path string
Hash string
}
// 计算文件的哈希值
func hashFile(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
// 扫描目录并计算文件的哈希值
func scanDirectory(root string) (map[string][]FileInfo, error) {
hashMap := make(map[string][]FileInfo)
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
isHL, err2 := checkHardLink(path)
if err2 != nil {
return err2
}
if isHL == true {
return nil
}
hash, err := hashFile(path)
if err != nil {
return err
}
hashMap[hash] = append(hashMap[hash], FileInfo{Path: path, Hash: hash})
return nil
})
return hashMap, err
}
// 打印重复文件并让用户选择保留哪些文件
func processDuplicates(duplicates map[string][]FileInfo) {
for hash, files := range duplicates {
if len(files) <= 1 {
continue
}
fmt.Printf("发现重复文件(哈希值: %s):\n", hash)
for i, file := range files {
fmt.Printf("[%d] %s\n", i+1, file.Path)
}
// 将未保留的文件转换为硬链接
for i, file := range files {
if i != 1 {
continue
}
err := os.Remove(file.Path)
if err != nil {
fmt.Printf("无法删除文件: %s, 错误: %v\n", file.Path, err)
continue
}
err = os.Link(files[0].Path, file.Path)
if err != nil {
fmt.Printf("无法创建硬链接: %s -> %s, 错误: %v\n", files[0].Path, file.Path, err)
} else {
fmt.Printf("已将 %s 转换为硬链接\n", file.Path)
}
}
}
}
// checkHardLink 判断文件是否为硬链接
func checkHardLink(path string) (bool, error) {
// 打开文件
file, err := os.Open(path)
if err != nil {
return false, err
}
defer file.Close()
// 获取文件信息句柄
var fileInfo syscall.ByHandleFileInformation
err = syscall.GetFileInformationByHandle(syscall.Handle(file.Fd()), &fileInfo)
if err != nil {
return false, err
}
// 如果文件是硬链接,NumberOfLinks > 1
return fileInfo.NumberOfLinks > 1, nil
}
func Scan(root string) {
duplicates, err := scanDirectory(root)
if err != nil {
fmt.Printf("扫描目录时出错: %v\n", err)
return
}
// 过滤出重复文件
repeatedFiles := make(map[string][]FileInfo)
for hash, files := range duplicates {
if len(files) > 1 {
repeatedFiles[hash] = files
}
}
if len(repeatedFiles) == 0 {
fmt.Println("没有发现重复文件。")
return
}
processDuplicates(repeatedFiles)
fmt.Println("处理完成。")
}