使用find和xargs移动文件时出现“mv:无法查找”错误。
使用find和xargs移动文件时出现“mv:无法查找”错误。
我正在编写一个bash脚本,需要将源文件夹中的所有文件和文件夹(递归地)移动到目标文件夹中。
为了尽可能地使其健壮,并解决可能的“参数列表过长”错误,我选择使用“find”命令(安全地确定要移动的文件)管道到“xargs”(高效地将移动操作组合在一起)。我还使用“-print0”和“-0”来解决可能出现的空格问题。
我编写了以下测试脚本:
#!/bin/bash
# 创建测试源文件结构和目标文件夹
mkdir -p sourceDir/{subdir1,subdir\ with\ space\ 2}
mkdir -p destDir
touch sourceDir/subdir1/{a1.txt,noExtension1,file1\ with\ space.txt}
touch sourceDir/subdir\ with\ space\ 2/{a2.txt,noExtension2,file2\ with\ space.txt}
# 从源文件夹移动文件/文件夹到目标文件夹
find sourceDir -mindepth 1 -print0 | xargs -0 mv --target-directory=destDir
看起来似乎工作正常(即文件被移动),但是出现了很多错误,如下所示:
mv: 无法统计“sourceDir/subdir with space 2/file2 with space.txt”:没有那个文件或目录
mv: 无法统计“sourceDir/subdir with space 2/noExtension2”:没有那个文件或目录
mv: 无法统计“sourceDir/subdir with space 2/a2.txt”:没有那个文件或目录
mv: 无法统计“sourceDir/subdir1/file1 with space.txt”:没有那个文件或目录
mv: 无法统计“sourceDir/subdir1/noExtension1”:没有那个文件或目录
mv: 无法统计“sourceDir/subdir1/a1.txt”:没有那个文件或目录
对于我脚本中指定的源文件来说,难道不应该有一种方法可以在不生成错误的情况下执行此操作吗?
为什么会生成这些错误(因为实际上文件和文件夹确实被移动了)?
在使用find命令通过xargs移动文件时出现"mv: cannot stat"错误的原因是,当使用find
命令直接移动文件时,它会为每个单独的文件执行mv命令。而如果需要移动的文件数量很多,比如成千上万个文件,这样的操作会变得非常慢和低效。通过使用xargs(或者在-exec
中使用+
),可以提高效率。
解决这个问题的方法是,使用xargs命令来批量处理文件移动操作。通过将find命令的输出作为xargs的输入,并将mv命令作为参数传递给xargs,可以一次性移动多个文件,而不是为每个文件执行一次mv命令。
下面是一个示例命令,演示如何使用find和xargs来移动文件:
$ find [...] -print0 | xargs -0 mv -t [...]
在这个命令中,-print0
选项告诉find命令以空字符作为分隔符输出文件列表,-0
选项告诉xargs命令以空字符作为分隔符读取输入。-t
选项告诉mv命令将文件移动到指定的目标目录。
通过使用xargs来批量处理文件移动操作,可以大大提高效率,特别是当需要移动大量文件时。这种方法避免了为每个文件执行mv命令的开销,使移动操作更加高效。
希望这篇文章能够帮助你理解在使用find命令通过xargs移动文件时出现"mv: cannot stat"错误的原因,并提供了解决这个问题的方法。通过使用xargs来批量处理文件移动操作,可以提高效率并避免低效的单个文件移动操作。
问题出现的原因是原始的find命令列出了每个文件的路径,然后尝试移动每一个文件。一旦顶级文件夹被移动,就没有必要移动任何子文件夹或文件了,但脚本仍然在尝试移动它们。解决方法是添加-maxdepth 1选项,只移动顶级文件和文件夹。
下面是一个演示此问题的脚本(不执行实际的移动操作):
#!/bin/bash mkdir -p sourceDir/{subdir1/subsubdir1,subdir\ with\ space\ 2} touch sourceDir/subdir1/{subsubdir1/deeperFile.log,a1.txt,noExtension1,file1\ with\ space.txt} touch sourceDir/subdir\ with\ space\ 2/{a2.txt,noExtension2,file2\ with\ space.txt} touch sourceDir/rootFile.txt echo -e "\n--- Output of 'ls -1: sourceDir':" echo "$(ls -1 sourceDir)" echo -e "\n--- original incorrect attempt was trying to move each of the following:" find sourceDir -mindepth 1 echo -e "\n--- working attempt only needs to move each of the top level files/folders, everything underneath any folders will get moved automatically" find sourceDir -mindepth 1 -maxdepth 1
脚本输出如下:
--- Output of 'ls -1: sourceDir': rootFile.txt subdir1 subdir with space 2 --- original incorrect attempt was trying to move each of the following: sourceDir/rootFile.txt sourceDir/subdir with space 2 sourceDir/subdir with space 2/file2 with space.txt sourceDir/subdir with space 2/noExtension2 sourceDir/subdir with space 2/a2.txt sourceDir/subdir1 sourceDir/subdir1/file1 with space.txt sourceDir/subdir1/noExtension1 sourceDir/subdir1/a1.txt sourceDir/subdir1/subsubdir1 sourceDir/subdir1/subsubdir1/deeperFile.log --- working attempt only needs to move each of the top level files/folders, everything underneath any folders will get moved automatically sourceDir/rootFile.txt sourceDir/subdir with space 2 sourceDir/subdir1
如果只使用`-type d`选项,则会忽略任何顶级文件。没有指定`-maxdepth 1`会列出任何子子文件夹,并且移动它们会失败。可以查看下面的更新回答以获取完整详细信息。
在你的问题中,你只提到了目录。所以我添加了`-type d`选项。第一行说:“(递归地)移动所有文件和文件夹”,虽然在示例脚本中可能没有明确说明。
"mv: cannot stat"错误是在使用find命令通过xargs移动文件时出现的。出现这个问题的原因是,默认情况下find命令的输出是从父目录开始的,然后是包含的目录。在你的目录结构上运行简单的find命令会得到以下输出:
sourceDir/subdir1 sourceDir/subdir1/a1.txt sourceDir/subdir1/noExtension1 sourceDir/subdir1/file1 with space.txt sourceDir/subdir with space 2 sourceDir/subdir with space 2/noExtension2 sourceDir/subdir with space 2/file2 with space.txt
如果你将这个输出用于xargs或-exec,它会先处理父目录,然后处理其内容。在这种情况下,你的命令会正常执行,并将整个sourceDir/subdir1目录移动到destDir/subdir1。之后,在sourceDir中就没有像sourceDir/subdir1/a1.txt或sourceDir/subdir1/noExtension1这样的文件/目录了,因为它们已经被移动到destDir。
为了避免这种情况,可以使用-depth选项。
-depth 优先处理每个目录的内容,然后再处理目录本身。
在你的情况下,你可以添加-type d选项将顶层源目录移动到destDir。
find sourceDir -mindepth 1 -type d -print0 | xargs -0 mv --target-directory=destDir
或者
find sourceDir -mindepth 1 -type d -exec mv -t destDir "{}" \+
这样可以解决你的问题。
感谢你的反馈,但是这仍然不起作用。如果没有我自己回答中提到的-maxdepth 1,你的解决方案会列出任何子子文件夹,并且再次无法移动它们。请参考我的更新答案获取详细信息。
同意,-maxdepth 1会使它更通用。