Linux 复制目录结构:不用 cp 命令,rsync/find/tar 轻松搞定
2025-03-21 01:32:14
只复制目录结构,不复制文件?安排!
直接上问题:怎样把一个地方的文件夹结构(注意,只要结构,不要文件!),原封不动地搬到另一个地方?我有一个好几TB的文件服务器,想在Linux系统上克隆它的目录结构,这个文件服务器是用CIFS挂载到Linux上的。cp --parents
可以移动文件和它的父目录结构,但是好像没办法完整复制整个目录树?
原因分析
咱得先搞清楚,为什么普通的 cp
命令不行。 cp
命令主要设计来复制文件,或者复制文件和它们的上级目录(通过--parents
)。 但是要复制整个,庞大的目录结构, 而不包含任何实际数据, cp
就有点力不从心了。 你也说了,服务器好几TB,要是全复制一遍,那得等到猴年马月?更何况,咱的目的只是要结构。
解决方案,安排!
下面这几个方法,都能帮你解决这个问题:
1. find
+ mkdir
:精细控制,一步到位
这个方法用 find
命令找到所有目录,然后用 mkdir
在目标位置创建对应的目录。非常直接,非常有效。
-
原理:
find
负责找到源目录下的所有目录。mkdir
的-p
参数,能够创建多级目录,并且如果目录已经存在,也不会报错。- 通过巧妙地构造路径和参数来完成目录结构的复制。
-
代码示例:
# 假设源目录是 /mnt/cifs/source,目标目录是 /home/user/destination find /mnt/cifs/source -type d -print0 | while IFS= read -r -d $'\0' dir; do mkdir -p "/home/user/destination/$(echo "$dir" | sed 's|/mnt/cifs/source/||')" done
-
代码解读
find /mnt/cifs/source -type d -print0
:/mnt/cifs/source
: 源路径-type d
:只查找目录。-print0
:用 null 字符分隔找到的目录名。这样可以安全地处理包含空格和其他特殊字符的目录名。
-
while IFS= read -r -d $'\0' dir; do ... done
:- 使用
while
循环处理find
命令的输出 IFS=
: 确保 read 不会处理输入中的空格。-r
: 告诉read
不转义反斜线。-d $'\0'
: 设置read
命令的分隔符为null字符
- 使用
-
mkdir -p "/home/user/destination/$(echo "$dir" | sed 's|/mnt/cifs/source/||')"
-p
: 确保父目录已创建/home/user/destination/
: 目标目录的前缀$(...)
: 将执行命令后的结果返回给命令echo "$dir"
: 输入变量sed 's|/mnt/cifs/source/||'
: 从目录路径中移除源目录部分,s|A|B|
表示使用 B 替换 A.
-
进阶用法 & 安全建议
-
如果你对要操作的目录很熟悉,可以用shell的参数展开,获得稍微快一点的速度:
find /mnt/cifs/source -type d -print0 | while IFS= read -r -d $'\0' dir; do mkdir -p "/home/user/destination/${dir#/mnt/cifs/source/}" done
${dir#/mnt/cifs/source/}
的作用与sed
指令的例子相同.
-
在执行前,最好先用
ls -ld /mnt/cifs/source/*
大概看看目录结构,心里有个数,避免出错。 -
执行时,一定一定要看清楚源目录和目标目录,别搞反了!
-
2. rsync
:专业选手,功能强大
rsync
本来是用来同步文件和目录的,但是它有一个非常巧妙的用法,可以只复制目录结构。
-
原理:
rsync
的-d
或--dirs
选项,可以复制目录,但不递归复制目录下的内容。- 搭配上
--include
和--exclude
参数实现我们需要的功能. - 我们巧妙地用
--include
包含所有目录,用--exclude
排除所有文件,就达到了只复制目录结构的目的。
-
代码示例:
rsync -av --include='*/' --exclude='*' /mnt/cifs/source/ /home/user/destination/
-
命令解读
-
rsync -av ...
:-a
: archive 模式,相当于-rlptgoD
,会保留各种文件属性。-v
: verbose 模式,显示详细信息。
*--include='*/'
: 包含所有以/
结尾的路径,也就是所有目录。
*--exclude='*'
: 排除所有其他东西(文件)。
*/mnt/cifs/source/
: 源目录,最后的斜杠不可省略.
*/home/user/destination/
: 目标目录
-
进阶技巧 & 安全建议:
rsync
有一个--dry-run
或-n
选项,可以先模拟执行,看看会发生什么,不会真的修改文件。强烈建议在实际执行前先模拟一下。
rsync -avn --include='*/' --exclude='*' /mnt/cifs/source/ /home/user/destination/
- 如果源目录或目标目录在网络上(比如通过NFS挂载),
rsync
也可以很好地工作。
3. tar
:打包解包,曲线救国
这个方法稍微绕一点,先把目录结构打包,然后再解包到目标位置。
-
原理:
tar
命令可以创建压缩包,我们可以创建一个只包含目录结构的压缩包。-f -
: 表示输出到标准输出(或从标准输入读取)。--null -T -
使用空字符作为分隔符-T
: 表示要操作的文件名从文件里读取
- 在解包的时候,只解压目录结构。
- 通过
find
指令与tar
配合可以高效创建仅有结构的包文件,再进行解包。
-
代码示例:
# 创建只包含目录结构的压缩包 find /mnt/cifs/source -type d -print0 | tar --null -T - -cf - | (cd /home/user/destination && tar -xf -)
-
指令解读
-
find /mnt/cifs/source -type d -print0
: 与方法1中的指令相同 -
tar --null -T - -cf -
:--null -T -
:将上一步的结果(使用null分隔)输入tar中.-c
: create, 创建一个新的归档文件。-f -
: 配合-c
使用,这里-
代表将结果输出到stdout。
-
(cd /home/user/destination && tar -xf -)
:(...)
开启一个子进程并在子进程内操作.cd /home/user/destination
:切换到目标路径下tar -xf -
:-x
: extract,解压缩归档文件。-f -
: 表示从标准输入中读取数据,并解压到当前文件夹下.
-
-
安全建议
- 因为存在中间打包, 所以比上面两种稍微慢一些。
4. 对于 BSD 系统的补充:pax
指令
-
pax
是比tar
更通用的可移植归档工具,在某些 BSD 家族系统中默认可用. 这种方法类似于方法 3 (tar
).find /mnt/cifs/source -type d -print0 | pax -rw -'s,.*/,,' -pe /home/user/destination
-
指令解读:
-
find /mnt/cifs/source -type d -print0
: 不再解释. -
pax -rw -'s,.*/,,' -pe /home/user/destination
-
-r
: 读取 -
-w
: 写入 -
-s
: 路径替代,后跟正则表达式 -
-'s,.*/,,'
: 从路径中移除所有路径,只保留文件夹的名字. -
-p
: 保留原属性,后可跟额外参数.
*-pe
: 保留所有模式和扩展属性
-
以上,几种方法, 任君选择. 看你喜欢哪个. 一般情况下, rsync
最方便,find + mkdir
最直观, tar
比较有趣.