0
点赞
收藏
分享

微信扫一扫

用golang实现一个---带黑白过滤器的----系统文件拷贝工具


设计目标:在大型项目中,会生成比较多的中间文件,例如.o, *.so, *.a, .pbg,.pdf, *.mp4等类型的文件, 在用tar打包或cp到其它路劲时,指向拷贝某一些种类型的文件。 本文就是实现这个功能。 磨刀不误砍柴工,看哪里不爽就去改造它。

​​项目源码托管地址:​​

编译安装tangcp系统工具

源码列表:

root@jack-VirtualBox:~/tangcp# tree
.
├── go.mod
├── go.sum
└── main.go

0 directories, 3 files
root@jack-VirtualBox:~/tangcp#

编译安装:

go build
mv tangcp /usr/bin/

首先看下cp的命令:

root@jack-VirtualBox:~/ll/copy_source_code_tools# cp --help
Usage: cp [OPTION]... [-T] SOURCE DEST
or: cp [OPTION]... SOURCE... DIRECTORY
or: cp [OPTION]... -t DIRECTORY SOURCE...
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

Mandatory arguments to long options are mandatory for short options too.
-a, --archive same as -dR --preserve=all
--attributes-only don't copy the file data, just the attributes
--backup[=CONTROL] make a backup of each existing destination file
-b like --backup but does not accept an argument
--copy-contents copy contents of special files when recursive
-d same as --no-dereference --preserve=links
-f, --force if an existing destination file cannot be
opened, remove it and try again (this option
is ignored when the -n option is also used)
-i, --interactive prompt before overwrite (overrides a previous -n
option)
-H follow command-line symbolic links in SOURCE
-l, --link hard link files instead of copying
-L, --dereference always follow symbolic links in SOURCE
-n, --no-clobber do not overwrite an existing file (overrides
a previous -i option)
-P, --no-dereference never follow symbolic links in SOURCE
-p same as --preserve=mode,ownership,timestamps
--preserve[=ATTR_LIST] preserve the specified attributes (default:
mode,ownership,timestamps), if possible
additional attributes: context, links, xattr,
all
--no-preserve=ATTR_LIST don't preserve the specified attributes
--parents use full source file name under DIRECTORY
-R, -r, --recursive copy directories recursively
--reflink[=WHEN] control clone/CoW copies. See below
--remove-destination remove each existing destination file before
attempting to open it (contrast with --force)
--sparse=WHEN control creation of sparse files. See below
--strip-trailing-slashes remove any trailing slashes from each SOURCE
argument
-s, --symbolic-link make symbolic links instead of copying
-S, --suffix=SUFFIX override the usual backup suffix
-t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY
-T, --no-target-directory treat DEST as a normal file
-u, --update copy only when the SOURCE file is newer
than the destination file or when the
destination file is missing
-v, --verbose explain what is being done
-x, --one-file-system stay on this file system
-Z set SELinux security context of destination
file to default type
--context[=CTX] like -Z, or if CTX is specified then set the
SELinux or SMACK security context to CTX
--help display this help and exit
--version output version information and exit

By default, sparse SOURCE files are detected by a crude heuristic and the
corresponding DEST file is made sparse as well. That is the behavior
selected by --sparse=auto. Specify --sparse=always to create a sparse DEST
file whenever the SOURCE file contains a long enough sequence of zero bytes.
Use --sparse=never to inhibit creation of sparse files.

When --reflink[=always] is specified, perform a lightweight copy, where the
data blocks are copied only when modified. If this is not possible the copy
fails, or if --reflink=auto is specified, fall back to a standard copy.
Use --reflink=never to ensure a standard copy is performed.

The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
The version control method may be selected via the --backup option or through
the VERSION_CONTROL environment variable. Here are the values:

none, off never make backups (even if --backup is given)
numbered, t make numbered backups
existing, nil numbered if numbered backups exist, simple otherwise
simple, never always make simple backups

As a special case, cp makes a backup of SOURCE when the force and backup
options are given and SOURCE and DEST are the same name for an existing,
regular file.

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Full documentation at: <https://www.gnu.org/software/coreutils/cp>
or available locally via: info '(coreutils) cp invocation'
root@jack-VirtualBox:~/ll/copy_source_code_tools#

虽然cp命令很强大。但是如果我想只拷贝其中的某些类型的文件,或不拷贝某些类型的文件,Linux自带的cp命令就无法满足。 cp是buxybox工具集里面的其中一个工具,实现的是通用性的功能,并非是他们没有能力写出来我的需求(Linux社区大神云集)。 既然是定制化的需求,那就自己动手吧!!!

于是写了一个工具叫:tangcp (没有叫tcp是因为tcp是网络协议栈、scp是super copy跨节点拷贝)

先看tangcp 工具的usage:

root@jack-VirtualBox:~# ./tangcp -h
NAME:
cp *.cpp, *.h, *c, *.cc

USAGE:
[global options] command [command options] [arguments...]

COMMANDS:
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--src value the source root path of the file to be copied
--dst value file copy destination path
--whitelist value whitelist filter key by file's type
--blacklist value blacklist filter key by file's type
--help, -h show help
root@jack-VirtualBox:~#

用法1,白名单机制:

只拷贝*.cpp、.h、.sh、.go、.java

./tangcp --src /root/ceph --dst /tmp/ceph --whitelist cpp,h,sh,go,java

用法2,黑名单机制:

拷贝除*.py、.png、.cpp之外的所有类型的文件:

./tangcp --src /root/ceph --dst /tmp/ceph --blacklist py,png,cpp

源码如下:

package main

import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/urfave/cli"
)

type filefilter struct {
mode string
item []string
}

func getFileInfo(src string) os.FileInfo {
if fileInfo, e := os.Stat(src); e != nil {
if os.IsNotExist(e) {
return nil
}
return nil
} else {
return fileInfo
}
}

func copyFile(src, dst string) bool {
if len(src) == 0 || len(dst) == 0 {
return false
}
srcFile, e := os.OpenFile(src, os.O_RDONLY, os.ModePerm)
if e != nil {
return false
}
defer func() {
srcFile.Close()
}()

dst = strings.Replace(dst, "\\", "/", -1)
dstPathArr := strings.Split(dst, "/")
dstPathArr = dstPathArr[0 : len(dstPathArr)-1]
dstPath := strings.Join(dstPathArr, "/")

dstFileInfo := getFileInfo(dstPath)
if dstFileInfo == nil {
if e := os.MkdirAll(dstPath, os.ModePerm); e != nil {
return false
}
}
dstFile, e := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
if e != nil {
return false
}
defer dstFile.Close()
if _, e := io.Copy(dstFile, srcFile); e != nil {
return false
} else {
return true
}
}

func copyPath(src, dst string, filter *filefilter) bool {
srcFileInfo := getFileInfo(src)
if srcFileInfo == nil || !srcFileInfo.IsDir() {
fmt.Println("parameter error")
return false
}
err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
relationPath := strings.Replace(path, src, "/", -1)
dstPath := strings.TrimRight(strings.TrimRight(dst, "/"), "\\") + relationPath
if !info.IsDir() {
basename := info.Name()
ss := strings.Split(basename, ".")
if len(ss) != 2 {
return nil
}
if filter.mode == "wl" {
flag := false
for _, fl := range filter.item {
if fl == ss[1] {
flag = true
break
}
}
if !flag {
return nil
}
} else if filter.mode == "bl" {
for _, fl := range filter.item {
if fl == ss[1] {
return nil
}
}
}

switch ss[1] {
case "cpp", "cc", "h", "hpp":
default:
return nil
}

if copyFile(path, dstPath) {
return nil
} else {
return errors.New(path + " copy fail")
}
} else {
if _, err := os.Stat(dstPath); err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(dstPath, os.ModePerm); err != nil {
return err
} else {
return nil
}
} else {
return err
}
} else {
return nil
}
}
})

if err != nil {
return false
}
return true

}

func main() {
app := &cli.App{
Name: "tangcp",
Usage: "./tangcp --src dir_src --dst dir_dst [--whitelist|--blacklist] [cpp,h,sh,go,java]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "src",
Usage: "the source root path of the file to be copied",
},
&cli.StringFlag{
Name: "dst",
Usage: "file copy destination path",
},
&cli.StringFlag{
Name: "whitelist",
Usage: "whitelist filter key by file's type",
},
&cli.StringFlag{
Name: "blacklist",
Usage: "blacklist filter key by file's type",
},
},
Action: func(cctx *cli.Context) error {
src := ""
dst := ""
wl := ""
bl := ""
if src = cctx.String("src"); src == "" {
return errors.New("--src src-dir")
}
if dst = cctx.String("dst"); dst == "" {
return errors.New("--src dst-dir")
}
wl = cctx.String("whitelist")
bl = cctx.String("blacklist")
if wl != "" && bl != "" {
return errors.New("cannot use whitelist and blacklist mode at the same time")
}
fmt.Println("start copy", src, dst)
copyPath(src, dst, &filefilter{mode: func() string {
if wl == "" && bl == "" {
return ""
} else if wl == "" && bl != "" {
return "bl"
} else {
return "wl"
}
}(),
item: func() []string {
if wl == "" && bl == "" {
return nil
}
filter_key := wl + bl
if len(filter_key) == 0 {
return nil
} else {
sList := strings.Split(filter_key, ",")
ss := make([]string, len(sList))
for i := range sList {
ss[i] = sList[i]
}

return ss
}
}(),
})

return nil
},
}
app.Setup()
app.Run(os.Args)
}


举报

相关推荐

0 条评论