find 批量复制报 are the same file:搜索结果包含了目标目录
GNU find 搜索树包含复制目标目录时,cp 会把目标文件再次复制到自身。先预览范围并剪枝,再处理同名覆盖、路径形式与命令退出状态。
用 find 找出某个目录下的文本文件,再交给 cp 复制到它的子目录,出现 are the same file。错误里的源路径和目标路径都指向收集目录中的同一个文件时,先检查搜索范围:目标目录本身也在源目录下面,find 会把已收集的文件再次作为输入。
下面讨论 Linux 上 GNU find 与 GNU cp 的本地复制。适用于按文件类型收集到一个目录的场景;若源与目标通过硬链接、符号链接或其他挂载指向同一个文件,还需要单独核对实际路径,不能仅排除一个目录名。
对照报错中的两条路径
以下是会包含目标文件的命令示例,不要直接在实际数据上重跑:
find /srv/source -type f -name '*.txt' -print0 |
xargs -0 cp -t /srv/source/collected
/srv/source/collected 仍位于搜索树内。只要它已有 note.txt,这个文件也符合 *.txt,cp 的目标又是 /srv/source/collected/note.txt,便成了复制到自身。
用 sort -u 去掉重复输入路径,无法排除一个只出现一次的目标文件。cp -u 控制更新条件,也没有定义 find 的搜索边界。把错误输出丢到 /dev/null 更不会阻止目标文件进入输入,还会藏掉权限或空间不足等错误。
如果布局允许,把收集目录放到搜索根目录之外,通常更直接。需要保留在源目录内部时,让 find 在到达目标目录时停止进入它。
先检查排除范围,再开始复制
下面假设 /srv/source/collected 已存在,是可信的普通目录,路径中没有 shell 模式字符,也没有用另一套符号链接路径访问同一目录。名称均为示例,需要按实际布局修改。
先只显示候选文件,不执行复制:
find /srv/source -path /srv/source/collected -prune -o \
-type f -name '*.txt' -print
左侧命中收集目录时,-prune 阻止向下遍历,-o 使右侧不再处理这个目录;其他路径才继续判断文件类型和名称。检查列表里没有 collected/ 中的文件,且应收集的源文件仍在。
-path 按 find 使用的整条路径进行模式匹配。搜索根写成 /srv/source,排除条件也采用同样的路径形式;不要一边从 . 开始,一边拿绝对路径来匹配。目标后面额外加 / 也可能不匹配。目录名含 *、? 或方括号时,不能把这个条件当作普通字符串等值比较。
-print 仅用于人工预览,带换行的文件名会让输出难辨认。实际复制用 -exec 直接传递参数,避免按空白拆分文件名。也不要增加 -depth:先遍历子树再处理目录,会使这里的剪枝失去作用。
确认已有目标数据有可恢复备份、源内容稳定、候选列表和同名文件处理规则正确后,才执行复制:
find /srv/source -path /srv/source/collected -prune -o \
-type f -name '*.txt' -exec cp -i -t /srv/source/collected -- {} +
{} + 分批把文件作为独立参数交给 cp,-t 固定目标目录。示例中的 -i 在覆盖已有文件前询问,因此适用于能看见并回应提示的受控终端;不要在无人值守任务里照搬它。自动任务需要先确定覆盖、保留或改名的规则,再选择相应选项。
此命令把文件按原来的文件名放到同一目录,不保留源目录层级。两个子目录里的同名文件仍会发生冲突,排除目标树并没有解决这件事。若需要完整目录结构或精确的属性保留,应改用项目认可的目录同步或归档流程,不能靠连续确认覆盖完成备份。
不再进入目标树,仍报错时查路径关系
先看新的错误是否仍来自 collected/。若是,核对 find 的起点、排除路径的拼写,以及是否通过不同路径访问目标;不要直接把排除规则扩大到整棵源树。
若错误发生在其他源文件上,源和目标可能通过链接等方式指向相同内容对象。应核对真实文件与目录关系,调整收集布局。GNU cp 通常拒绝把文件复制到自身,不应把强制覆盖或先删除源文件当成绕过办法。
修改后保留错误输出,并查看整个 find 命令的退出状态;有任一批 cp 失败,不能只因部分文件已出现就宣布全部完成。还要按候选清单检查实际文件内容、应有的数量和同名冲突处理结果。源会变化时,这次遍历也不是一致的备份快照。
需要撤销复制时,从执行前的目标备份恢复被覆盖文件。新增文件也只按本次清单逐项处理,保留目标里原有且不属于这次复制的内容;仅改回 find 参数不会撤销已经发生的写入。


