如果不同则为 mv,如果相同则为 rm

如果不同则为 mv,如果相同则为 rm

我有一个文件和目录树,我想mv path/to/src path/to/dest在 Linux 上使用它。

这已经尝试过了,结果一些(大)文件已经被移动或复制,一些文件已经被部分复制。

我想完成mv,但忽略任何已复制的文件,并且与源文件大小相同,并rm代替他们。

IE

move_or_rm (src, dst):
    mkdir dst
    for each file f in src/*:
        if dst/f does not exist or dst/f is a different size from src/f:
            mv src/f dst/f
        else:
            rm src/f
    for each directory d in src/*:
        move_or_rm (src/d, dst/d)

(其中 f 是path/to/some/file相对于 的src

我可以在脚本中完成这个,但是有没有办法使用标准 Linux 命令行工具来完成呢?

答案1

脚本中的任何内容都可以“压缩”到一行中,用 分隔命令;。但是,在这种情况下,我建议使用脚本,因为它涉及一些最不复杂的逻辑。

要检查文件是否存在,请使用-f比较器。要检查两个文件是否相同,我会使用命令md5sum。其中一个文件中的任何不同位都会产生完全不同的 md5 总和,因此如果它们匹配,则意味着它们是同一个文件。所以:

if dst/f does not exist or dst/f is a different size from src/f:

翻译如下:

if [ -f dst/f ] || [[ `md5sum src/f` != `md5sum dst/f` ]]; then
  cp src/f dst/f
fi

答案2

我会看看是否有人发布更简洁的命令行答案,但与此同时,这是我想到的:

#!/usr/bin/env python3
import os
import sys

COMMIT = False



def try_mv (src, dst):

    print ("mv {} {}".format (src, dst))

    if COMMIT:

        try:

            os.rename (src, dst)

        except Exception as e:

            print ("\t{}".format(e))

            return False

    return True



def try_rm (src):

    print ("rm " + src)

    if COMMIT:

        try:

            os.remove (src)

        except Exception as e:

            print ("\t{}".format(e))

            return False

    return True



def try_rmdir (src):

    print ("rmdir " + src)

    if COMMIT:

        try:

            os.rmdir (src)

        except Exception as e:

            print ("\t{}".format (e))

            return False

    return True



def mv_or_rm_if_exists (src_dir, dst_dir):

    if not os.path.isdir (src_dir):
        raise Exception (src_dir + " is not a directory.")

    if not os.path.isdir (dst_dir):

        if os.path.exists (dst_dir):
            raise Exception (dst_dir + " exists but is not a directory.")

        if COMMIT:
            os.mkdir (dst_dir)

    for name in os.listdir (src_dir):

        src_path = src_dir + "/" + name
        dst_path = dst_dir + "/" + name

        if os.path.isdir (src_path):

            mv_or_rm_if_exists (src_path, dst_path)

            try_rmdir (src_path)

        elif not os.path.isfile (src_path):

            print ("Skipping non-file " + src_path)

        elif not os.path.exists (dst_path):

            try_mv (src_path, dst_path)

        elif not os.path.isfile (dst_path):

            print ("Skipping {} because {} is not a file".format (src_path, dst_path))

        else:

            # src_path and dst_path both exist and are both files
            
            if os.path.getmtime (dst_path) > os.path.getmtime (src_path):
                print ("Skipping {} because its mtime is greater than that of {}".format (dst_path, src_path))
            elif os.path.getsize (src_path) == os.path.getsize (dst_path):
                try_rm (src_path)
            else:
                try_mv (src_path, dst_path)



if __name__ == "__main__":

    this_script = sys.argv.pop (0)

    # --commit may be first or last

    if sys.argv[0] == "--commit":
        COMMIT = True
        sys.argv.pop(0)

    if len (sys.argv) == 3 and sys.argv[2] == "--commit":
        COMMIT = True
        sys.argv.pop (2)

    if len (sys.argv) != 2:
        print ("Usage: {} (--commit) src dst".format (this_script))

    mv_or_rm_if_exists (sys.argv[0], sys.argv[1])
    try_rmdir (sys.argv[0])

答案3

该函数仅包装一个rsync命令,它将执行您想要的操作。

move_or_rm()
{
    local src="$1" dst="$2"
    rsync -rvH --remove-source-files "$src/ "$dst"
}

如果也包含文件的日期/时间就更可靠了,在这种情况下您可以-rvH用替换-rtvH

为了测试它,您可以添加选项--dry-runrsync它将尝试向您展示它想要做什么而无需实际执行。

相关内容