如何在Bash中从指定目录开始复制文件并保留目录结构?
解决Bash中复制文件并保留指定目录后路径结构的问题
你提到用cp --parent会把完整路径(包括first目录)都复制过去,确实这个参数是保留从当前工作目录到文件的完整层级。下面给你两个实用的解决方案,分别用rsync(最简洁高效)和find+cp(更灵活)来实现:
方案一:用rsync快速裁剪路径前缀
rsync天生擅长处理这种路径结构的需求,尤其是--strip-components参数可以精准去掉你不需要的前缀层级,完美匹配你的需求:
假设你的源文件都在/first目录下,目标目录是destination_folder,执行这条命令即可:
rsync -a --include='*.txt' --exclude='*' --strip-components=1 /first/ destination_folder/
参数解释:
-a:归档模式,会保留文件的权限、时间戳等属性,相当于同时启用了递归、保留链接、权限等多个参数。--strip-components=1:去掉源路径的第一层目录(也就是first),这样复制后的文件路径就只剩下/first/之后的部分。--include='*.txt'和--exclude='*':用来过滤只复制.txt文件,如果需要复制所有文件,直接删掉这两个参数就行。
如果你的文件是通过find命令筛选出来的(比如有更复杂的条件),也可以把find的输出传给rsync:
find /first -type f -name "*.txt" | rsync -aR --files-from=- . destination_folder/
这里--files-from=-表示从标准输入读取文件列表,-R(--relative)会保留相对路径,再配合--strip-components=1的话,记得调整源的基准路径,或者直接用前面的命令更简单。
方案二:用find+循环裁剪路径(适合自定义逻辑)
如果你更习惯用find和cp,可以通过Bash的字符串截取功能先裁剪掉不需要的前缀,再创建目录复制文件:
find /first -type f -name "*.txt" | while read -r file; do # 裁剪掉开头的/first/前缀,得到我们需要的相对路径 relative_path="${file#/first/}" # 拼接目标文件的完整路径 target_file="destination_folder/$relative_path" # 创建目标文件所在的目录(如果不存在) mkdir -p "$(dirname "$target_file")" # 复制文件,-a保留属性 cp -a "$file" "$target_file" done
关键细节:
${file#/first/}:这是Bash的参数扩展语法,作用是移除file变量开头匹配/first/的部分,精准得到我们需要的路径。mkdir -p:确保目标目录存在,即使嵌套多层也会自动创建,避免复制时因为目录不存在报错。- 所有变量都用双引号包裹:防止路径中有空格、特殊字符时出现解析错误,这是Bash脚本的好习惯。
测试建议
不管用哪种方法,都可以先做个测试验证路径是否正确:
- 对
rsync,加上--dry-run参数,它会模拟复制过程但不实际操作,你可以看到输出的路径是否符合预期:rsync -a --dry-run --include='*.txt' --exclude='*' --strip-components=1 /first/ destination_folder/ - 对
find+cp,把cp -a换成echo cp -a,这样会打印出要执行的复制命令,确认没问题再去掉echo。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

