以下面的目录树为例,将内容移动directory/folderA
到的最佳方法是什么directory
。
如果目标中存在同名文件,如何覆盖文件,例如:directory/folderA/2017/06/info.log
和directory/2017/06/info.log
。
directory
|-- folderA
| |-- 2017
| | |-- 06
| |
| |-- 2016
| | |-- 12
| | |-- 11
| | |-- 10
|
|-- 2017
| |-- 04
| |-- 05
| |-- 06
|
|-- 2016
| |
答案1
您可以使用cp -l
( --link
,硬链接文件而不是复制):
cp -rfl folderA .
rm -rf folderA
只是mv
移动,即将目录重命名为您的directory
。对于在同一个文件系统内移动文件,mv
使用rename()
系统调用。如果源和目标位于不同的文件系统上,mv
将使用cp
和rm
完成移动,但仍然首先删除目标,复制硬链接文件(-R
)但不遵循符号链接(-P
):
rm -f destination_path && \ cp -pRP source_file destination && \ rm -rf source_file
虽然您无法更改 的行为mv
,cp
但命令本身更灵活,您确实应该使用它。使用cp
,-f
会导致删除和创建目标中的单个文件,而不是先删除整个目标。
也cp
有限制,因为它只能覆盖 ( -f
)、保存 ( -n
) 或询问 ( -i
)。如果您需要比较文件才能决定保留哪个文件,则需要rsync
。
如果您仍然需要使用rename()
系统调用但要按要求完成合并目录的目标,则需要编写一个脚本来调用单个rename()
文件的单独脚本。
答案2
首先,我使用以下方法创建了目录树
[root@localhost /tmp]# mkdir -pv directory/folderA/2017/06 directory/folderA/2016/{10..12} directory/2017/{04..06} directory/2016
然后我使用以下命令创建了示例文件:
[root@localhost /tmp]# touch directory/{,folderA}/2017/06/info.log
最后,我使用带有 --delete-after 的 rsync 来移动,而不是使用 rsync 的默认行为(即创建副本):
[root@localhost /tmp]# rsync --delete-after -a directory/folderA/ directory
注意:如果您希望在复制目录后删除目录,则需要在源目录后面加上尾随的“/”(即 directory/folderA/ 而不是 directory/folderA)。
当然,如果您确实想输入 mv 来获得所需的行为,您可以创建自定义 shell 函数。我并不提倡这样做,但这是可能的。虽然有点草率,但确实可能。
这是您可以添加到 ~/.bashrc 的自定义函数:
function mv() {
if [[ "$@" =~ .*--delete-after.*-av*.* ]]; then
rsync $@
else
command mv $@
fi; }
然后通过注销并重新登录来调用更新的 ~/.bashrc - 或者 - 输入:
[root@localhost /tmp]# exec bash
现在,您可以使用以下任一方式测试您的自定义函数:
[root@localhost /tmp]# mv --delete-after -av directory/folderA/ directory
为了获得详细输出,
或者:
[root@localhost /tmp]# mv --delete-after -a directory/folderA/ directory
仅接收遇到的错误。
另外,您还可以通过在 cp 命令前面加上反斜杠来避免将 yes 传递给 cp -rf 命令,如下所示:
[root@localhost /tmp]# \cp -rf directory/folderA/* directory/
您使用管道传输“是”的原因是您可能在 ~/.bashrc 中有一个 cp 命令的别名,如下所示:
[root@localhost /tmp]# alias cp='cp -i'
您可以通过输入以下内容进行验证:
[root@localhost /tmp]# type cp