golang 多线程 备份文件夹到兄弟层级 wail group
D:\GolangTools\src\config\config.go
package config
type ConfigHandler struct {
includeDirNames []string
includeFileNames []string
excludeDirNames []string
excludeFileNames []string
}
func NewConfigHandler(includeDirNames []string, includeFileNames []string, excludeDirNames []string, excludeFileNames []string) *ConfigHandler {
return &ConfigHandler{
includeDirNames: includeDirNames,
includeFileNames: includeFileNames,
excludeDirNames: excludeDirNames,
excludeFileNames: excludeFileNames,
}
}
func (my *ConfigHandler) SetIncludeDirNames(includeDirNames []string) {
my.includeDirNames = includeDirNames
}
func (my *ConfigHandler) SetIncludeFileNames(includeFileNames []string) {
my.includeFileNames = includeFileNames
}
func (my *ConfigHandler) SetExcludeDirNames(excludeDirNames []string) {
my.excludeDirNames = excludeDirNames
}
func (my *ConfigHandler) SetExcludeFileNames(excludeFileNames []string) {
my.excludeFileNames = excludeFileNames
}
func (my *ConfigHandler) GetIncludeDirNames() []string {
return my.includeDirNames
}
func (my *ConfigHandler) GetIncludeFileNames() []string {
return my.includeFileNames
}
func (my *ConfigHandler) GetExcludeDirNames() []string {
return my.excludeDirNames
}
func (my *ConfigHandler) GetExcludeFileNames() []string {
return my.excludeFileNames
}
D:\GolangTools\src\input\inputHandler.go
package input
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
"time"
)
type IInputHandler interface {
HandleInputArgs()
}
type InputHandler struct {
function string
inputPath string
dstFolderPath string
}
func (my *InputHandler) HandleInputArgs() error {
// os.Args 是一个字符串切片,存储了命令行参数
// 其中 os.Args[0] 是程序本身的名称,后续元素是传递的参数
args := os.Args
if len(args) < 3 {
return fmt.Errorf("参数不足,请输入两个参数")
}
for i, arg := range args {
if i == 0 {
continue
}
// fmt.Printf("参数 %d 是: %s\n", i, arg)
parts := strings.Split(arg, "=")
if len(parts) != 2 {
return fmt.Errorf("参数格式错误,请使用格式: key=value")
}
switch parts[0] {
case "--function":
my.function = parts[1]
continue
case "--folderpath":
my.inputPath = parts[1]
continue
default:
return fmt.Errorf("未知参数: %s", parts[0])
}
}
return nil
}
func NewInputHandler() *InputHandler {
return &InputHandler{}
}
func (my *InputHandler) GetFunction() string {
return my.function
}
func (my *InputHandler) GetInputPath() string {
return my.inputPath
}
func (my *InputHandler) SetFunction(function string) {
my.function = function
}
func (my *InputHandler) SetInputPath(inputPath string) {
my.inputPath = inputPath
}
func (my *InputHandler) SetDstFolderPath() {
fmt.Print("请输入注解后缀:")
suffix, _ := bufio.NewReader(os.Stdin).ReadString('\n') // 读取直到换行符
suffix = strings.TrimSpace(suffix) // 去除字符串两端的空白字符,包括换行符
originlFolderName := filepath.Base(my.inputPath)
fatherFolderPath := filepath.Dir(my.inputPath)
my.dstFolderPath = filepath.Join(fatherFolderPath, originlFolderName+"_"+time.Now().Format("20060102_150405")) + "_" + suffix
}
func (my *InputHandler) GetDstFolderPath() string {
return my.dstFolderPath
}
D:\GolangTools\src\path\pathHandler.go
package path
import (
"GolangTools/src/config"
"GolangTools/src/input"
"GolangTools/src/utils"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
type IPathHandler interface {
GetAllCodeFiles() []string
}
type PathHandler struct {
allFilePaths []string
inputHandler *input.InputHandler
configHandler *config.ConfigHandler
}
func NewPathHandler(inputHandler *input.InputHandler, configHandler *config.ConfigHandler) *PathHandler {
return &PathHandler{
inputHandler: inputHandler,
configHandler: configHandler,
}
}
func (my *PathHandler) GetAllCodeFiles() ([]string, error) {
inputPath := my.inputHandler.GetInputPath()
// 使用 os.ReadDir 读取目录
topDirs, err := my.GetTopDirs(inputPath)
if err != nil {
return nil, err
}
var producerWg sync.WaitGroup
var consumerWg sync.WaitGroup
var copyWg sync.WaitGroup
// 创建一个容量为 3 的有缓冲的 int 类型 channel
bufferedChannel := make(chan string, 100)
for _, topDir := range topDirs {
folderName := filepath.Base(topDir)
if utils.ExsitInSlice(my.configHandler.GetExcludeDirNames(), folderName) {
if !utils.ExsitInSlice(my.configHandler.GetIncludeDirNames(), folderName) {
continue
} else {
println("skip:", filepath.Join(inputPath, folderName))
}
}
producerWg.Add(1)
go func(topDir string) {
defer producerWg.Done()
my.TraverseDir(topDir, bufferedChannel, &producerWg)
}(topDir)
}
consumerWg.Add(1)
go func() {
defer consumerWg.Done()
my.CollectDeepDirs(bufferedChannel, &consumerWg)
}()
// 关闭通道的 goroutine
go func() {
producerWg.Wait()
close(bufferedChannel)
}()
consumerWg.Wait()
// 关闭 channel
println("收集完毕Done,开始拷贝!!!")
// println(my.inputHandler.GetDstFolderPath())
topFiles, err := my.GetTopFiles(my.inputHandler.GetInputPath())
if err != nil {
println(err.Error())
return nil, err
}
my.allFilePaths = append(my.allFilePaths, topFiles...)
for _, filePath := range my.allFilePaths {
copyWg.Add(1)
go func(copyWg *sync.WaitGroup) {
defer copyWg.Done()
parts := strings.Split(filePath, my.inputHandler.GetInputPath())
dstFilePath := filepath.Join(my.inputHandler.GetDstFolderPath(), parts[1])
_, err := utils.CopyFile(filePath, dstFilePath, true)
if err != nil {
println("copy error:" + filePath + err.Error())
}
println("copy:" + filePath)
}(©Wg)
}
copyWg.Wait()
return nil, nil
}
func (my *PathHandler) GetTopFiles(inputPath string) ([]string, error) {
entries, err := os.ReadDir(inputPath)
if err != nil {
return nil, fmt.Errorf("读取目录:%s出错:%v", inputPath, err)
}
folderPaths := make([]string, 0)
// 遍历目录中的条目
for _, entry := range entries {
if !entry.IsDir() {
// fmt.Println("子文件夹:", entry.Name())
// fmt.Println("子文件夹:", filepath.Join(inputPath, entry.Name()))
folderPaths = append(folderPaths, filepath.Join(inputPath, entry.Name()))
}
}
return folderPaths, nil
}
func (my *PathHandler) GetTopDirs(inputPath string) ([]string, error) {
entries, err := os.ReadDir(inputPath)
if err != nil {
return nil, fmt.Errorf("读取目录:%s出错:%v", inputPath, err)
}
folderPaths := make([]string, 0)
// 遍历目录中的条目
for _, entry := range entries {
if entry.IsDir() {
// fmt.Println("子文件夹:", entry.Name())
// fmt.Println("子文件夹:", filepath.Join(inputPath, entry.Name()))
folderPaths = append(folderPaths, filepath.Join(inputPath, entry.Name()))
}
}
return folderPaths, nil
}
func (my *PathHandler) TraverseDir(inputPath string, c chan<- string, wg *sync.WaitGroup) error {
entries, err := os.ReadDir(inputPath)
if err != nil {
return fmt.Errorf("读取目录:%s出错:%v", inputPath, err)
}
// folderPaths := make([]string, 0)
// 遍历目录中的条目
for _, entry := range entries {
if entry.IsDir() {
folderName := entry.Name()
if !utils.ExsitInSlice(my.configHandler.GetExcludeDirNames(), folderName) {
my.TraverseDir(filepath.Join(inputPath, folderName), c, wg)
continue
}
if !utils.ExsitInSlice(my.configHandler.GetIncludeDirNames(), folderName) {
println("skip:", filepath.Join(inputPath, folderName))
continue
}
my.TraverseDir(filepath.Join(inputPath, folderName), c, wg)
} else {
fileName := entry.Name()
if !utils.ExsitInSlice(my.configHandler.GetExcludeFileNames(), fileName) {
// println("copy:", filepath.Join(inputPath, fileName))
c <- filepath.Join(inputPath, fileName)
continue
}
if !utils.ExsitInSlice(my.configHandler.GetIncludeFileNames(), fileName) {
println("skip:", filepath.Join(inputPath, fileName))
continue
}
c <- filepath.Join(inputPath, entry.Name())
}
}
return nil
}
func (my *PathHandler) CollectDeepDirs(c <-chan string, wg *sync.WaitGroup) error {
for data := range c {
my.allFilePaths = append(my.allFilePaths, data)
}
return nil
}
D:\GolangTools\src\utils\utils.go
package utils
import (
"fmt"
"io"
"os"
"path/filepath"
)
func ExsitInSlice(slice []string, str string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}
func CopyFile(src, dst string, overwrite bool) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
// Check if source and destination are the same file
if !overwrite {
destFileStat, err := os.Stat(dst)
if err == nil { // 如果目标文件存在
if os.SameFile(sourceFileStat, destFileStat) {
return 0, fmt.Errorf("source and destination are the same file")
}
} else if !os.IsNotExist(err) { // 如果不是因为目标文件不存在而产生的错误
return 0, err
}
}
// 打开源文件
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
// 创建或打开目标文件
// 确保目标路径的所有父目录都存在
if err := os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil {
return 0, fmt.Errorf("creating parent directories for destination %s: %v", dst, err)
}
var flags int = os.O_CREATE | os.O_WRONLY
if overwrite {
flags |= os.O_TRUNC // 如果允许覆盖,则截断文件
} else {
flags |= os.O_EXCL // 如果不允许覆盖,则使用 O_EXCL 防止覆盖
}
destination, err := os.OpenFile(dst, flags, sourceFileStat.Mode())
if err != nil {
return 0, err
}
defer destination.Close()
// 复制文件内容
nBytes, err := io.Copy(destination, source)
return nBytes, err
}
D:\GolangTools\go.mod
module GolangTools
go 1.23.0
D:\GolangTools\main.go
package main
import (
"GolangTools/src/config"
"GolangTools/src/input"
"GolangTools/src/path"
"fmt"
"time"
)
func main() {
start := time.Now()
var includeDirNames []string = []string{"", ""}
var includeFileNames []string = []string{"", ""}
var excludeDirNames []string = []string{".git", "obj", "bin", ".vscode", "zz_note", "zz_zz", "zz_pro", "zz_res", "zz_config"}
var excludeFileNames []string = []string{"", ""}
configHandler := config.NewConfigHandler(includeDirNames, includeFileNames, excludeDirNames, excludeFileNames)
inputHandler := input.NewInputHandler()
err := inputHandler.HandleInputArgs()
if err != nil {
panic(err)
}
inputHandler.SetDstFolderPath()
pathHandler := path.NewPathHandler(inputHandler, configHandler)
pathHandler.GetAllCodeFiles()
duration := time.Since(start)
fmt.Printf("程序执行时间: %v\n", duration)
}
D:\GolangTools\run.bat
@REM set scriptPath=D:\DotnetTools
set scriptPath=%cd%
go run main.go --function=code2md --folderpath="%scriptPath%"
标签:group,string,err,备份文件,func,inputPath,return,多线程,my
From: https://www.cnblogs.com/zhuoss/p/18682584