我有一个文件和目录树,我想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-run
,rsync
它将尝试向您展示它想要做什么而无需实际执行。