【问题标题】:"mv: cannot stat" error when using find to move files via xargs使用 find 通过 xargs 移动文件时出现“mv: cannot stat”错误
【发布时间】:2014-08-08 16:27:31
【问题描述】:

我正在编写一个 bash 脚本,该脚本需要(递归地)将源文件夹中的所有文件和文件夹移动到目标文件夹中。

在尝试使其尽可能健壮并解决潜在的argument list too long - errors 问题时,我选择使用find 命令(以安全地确定要移动的文件)通过管道传输到xargs(以有效地将移动组合在一起)。我还使用-print0-0 来解决空间的潜在问题。

我编写了以下测试脚本:

#!/bin/bash

# create test source file structure, and destination folder
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}

#move files/folders from source to destination
find sourceDir -mindepth 1 -print0 | xargs -0 mv --target-directory=destDir

这似乎可行(即文件被移动)但是由于某种原因我收到了许多错误,如下所示:

mv: cannot stat `sourceDir/subdir with space 2/file2 with space.txt': No such file or directory
mv: cannot stat `sourceDir/subdir with space 2/noExtension2': No such file or directory
mv: cannot stat `sourceDir/subdir with space 2/a2.txt': No such file or directory
mv: cannot stat `sourceDir/subdir1/file1 with space.txt': No such file or directory
mv: cannot stat `sourceDir/subdir1/noExtension1': No such file or directory
mv: cannot stat `sourceDir/subdir1/a1.txt': No such file or directory

是否应该没有办法做到这一点(对于我的脚本中指示的源文件)而不产生错误?

为什么会产生这些错误(因为文件和文件夹实际上正在被移动)?

【问题讨论】:

  • find sourceDir -mindepth 1 -exec mv "{}" --target-directory=destDir \;替换你的find命令
  • 与上述脚本一起使用时仍然会出错。

标签: bash find xargs stat mv


【解决方案1】:

为什么不直接使用find 命令呢?

$ find [...] -exec mv '{}' [...] ';'

其中{} 表示找到的每个文件,; 表示传递给mv 的参数的结尾

【讨论】:

  • 回答为什么不直接使用find 执行此操作 - 如果我只是将 find 与 mv 一起使用,它将为每个单独的文件执行 mv 命令。如果有数千或数十万个文件要移动,这将很快变得缓慢/低效。通过 xargs (或使用 +-exec 是改进这一点的方法)。有关更多详细信息,请参阅此答案:stackoverflow.com/a/11942775/1448678
【解决方案2】:

我已经猜到了。

简答:我错过了-maxdepth 1 标签。以下命令有效

find sourceDir -mindepth 1 -maxdepth 1 -print0 | xargs -0 mv --target-directory=destDir
# as does
find sourceDir -mindepth 1 -maxdepth 1 -exec mv --target-directory=destDir '{}' +

更长的答案:

我最初的查找命令是列出每个文件的路径,然后尝试移动每个文件。一旦移动了顶级文件夹,就不需要移动下面的任何文件/文件夹 - 但脚本仍在尝试!

下面的脚本演示了这一点(没有实际执行移动):

#!/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 来查找顶级目录。在下面检查我的答案。
  • 只有-type d 这将忽略任何顶级文件。同样没有 -maxdepth 1 在我自己的答案中说明,您的解决方案仍将列出所有子子文件夹 - 并且再次尝试移动它们失败。有关详细信息,请参阅我的更新答案。
  • 在您的问题中,您只提到了目录。所以我加了-type d
  • 第一行说:“(递归地)移动所有文件和文件夹”,虽然我在示例脚本中可能没有说清楚。
【解决方案3】:

默认情况下 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/subdire1。之后,sourceDir 中没有像 sourceDir/subdir1/a1.txt 或 sourceDir/subdir1/noExtension1 这样的文件/目录,因为它已经移动到 destDir。

为避免这种情况,请使用-depth 选项。

-depth Process each directory's contents before the directory itself.

在您的情况下,您可以添加 -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,您的解决方案将列出所有子子文件夹 - 并且再次尝试移动它们失败。有关详细信息,请参阅我的更新答案。
  • 同意。 -maxdepth1 会让它更通用。
猜你喜欢
  • 2016-05-08
  • 2020-01-31
  • 2016-07-27
  • 1970-01-01
  • 1970-01-01
  • 2016-03-15
  • 1970-01-01
  • 2020-12-09
  • 2014-05-17
相关资源
最近更新 更多