如何根据文件夹名称将未知文件移动到未知目录?

如何根据文件夹名称将未知文件移动到未知目录?

我正在尝试编写一个脚本,该脚本将在目录中搜索文件夹,并将文件夹内容从其位置向上移动一个目录。这是我目前所拥有的:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

我认为我的问题在于目标目录..。也许我表达错了?

任何帮助都是有用的。

*编辑**

为了简化这一点,我将介绍更多信息

我所在的组织正在尝试从 MS Exchange 迁移到 Zimbra。在迁移过程中,我们发现了一些很棒的邮箱传输实用程序,但将 PST 文件转换为 Zimbra 可用格式却颇具挑战性。

我已经找到了一些解决方案,长话短说,我能够将 pst 文件转换为可用的格式,但有一个例外 -

不会将邮件放入该文件夹,而是会创建一个子目录,该子目录“mbox”可能类似于 .eml 文件所从中分离出来的 mbox 文件。由于每个用户都不同,并且使用与其他用户不同的文件夹结构,因此在脚本运行之前,每个目录都是未知的。此外,mbox 文件夹的内容也是唯一的。

由于名为 mbox 的文件夹是这些目录中唯一不变的文件夹,因此我尝试使用 find 来定位这些文件夹,获取该文件夹的内容并将其上移一个目录,然后删除名为 mbox 的文件夹。以下是我的脚本中显示的实际命令:

find localFolders -name mbox -type d -exec mv {}/* .. \;

或者在您刚刚给出的例子中:

find localFolders -name mbox -type d -execdir sh -c 'echo mv {}/* ..' \;

我尝试使用*通配符来选择 mbox 文件夹中的所有内容。据我所知,它被视为目录的一部分,而不是预期的通配符

想法?

答案1

这个怎么样?

find ./localFolders -name 'file' -type d -execdir bash -c 'mv -vnt . -- "$0"/*' {} \; -prune

文件名中空格和奇怪符号是安全的。尽情享受吧!

答案2

为了正确地做你想做的事情,find你必须了解以下概念:

  1. 进程的工作目录。
  2. bash(或其他 shell)的 glob 特性。

Linux 中的每个进程都有一个工作目录作为其内部状态的一部分。您可以将其想象为文件系统中进程的“位置”。shell 也是一个进程,也有一个工作目录。shell 的工作目录是(通常)在您的提示符中显示的目录。

当一个进程被创建时,它将从其父进程继承工作目录。如果你从 shell 启动一个进程,那么 shell 就是父进程。每个进程都可以随意更改自己的工作目录。你可以使用内置命令更改 shell 的工作目录cd

处理相对文件名时会用到工作目录。相对文件名是不以 开头的文件名/。例如,当您执行 时,它会在当前工作目录中cat foo查找文件,但当您执行 时,它会在 中查找文件。foocat /tmp/foofoo/tmp

处理时,find -exec你总是要考虑工作目录。 的工作目录-exec是 的工作目录find。 的工作目录find是 shell 的工作目录。

就你的情况而言:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

执行命令时,将引用工作目录的父目录,该工作目录是 shell 的工作目录。这不是您..想要的mv-execfind

有一个-execdir类似的-exec,但它首先将工作目录切换到找到匹配项的目录。-execdir是解决您的问题的一大步,但您的find命令中还有另一个问题:*又名 glob。

glob 类似于通配符。例如,foo*将匹配foobarfoobaz(并且foo因为*也匹配空字符串)。到目前为止一切顺利。globbing 的有趣之处在于它是由 shell 而不是执行的命令完成的。当您执行时,rm foo*shell 将首先将扩展为*foobarfoobaz然后执行rm foobar foobazrm永远不会看到*

如果当前目录中没有以 开头的文件,会发生什么情况foo?那么 shell(通常)不会扩展 ,*而是逐字逐句地将 传递*给命令。这意味着rm foo*将以 执行rm foo*rm它将查找名为 的文件foo*,并且(很可能)找不到该文件并中止并显示错误。如果您想知道:是的,文件名可以包含*,不,不要在家尝试。

就你的情况而言:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

在执行find命令之前,shell 会先尝试扩展 glob {}/*。没有符合模式的文件{}/*,因此 glob 会被逐字传递。

让我们看看该find命令的作用。假设您正在执行find命令,/home/username/somedir并且存在一个/home/username/somedir/localFolders/foodir/file包含一些文件的目录。find将开始搜索,/home/username/somedir/localFolders因为参数中是这样说的,但工作目录find/home/username/somedir,因为它是从那里执行的。

现在find找到了目录/home/username/somedir/localFolders/foodir/file。在执行之前,它会用找到的项目-exec替换。这意味着将变成。将失败,因为它(很可能)找不到名为的文件。{}mv {}/* ..mv /home/username/somedir/localFolders/foodir/file/* ..mv*

假设有一个名为的文件*,则将mv文件移动到..。请记住,工作目录是/home/username/somedir。这意味着mv将文件移动到/home/username/somedir/../home/username这不是您想要的。

命令的“修复”find取决于几个因素。例如:

  • 目录中是否有“点文件” file?点文件是名称以点开头的文件。 glob*通常不会扩展到以点开头的文件名。
  • 目录中还有其他目录吗filefind将尝试递归到它们但会失败,因为它们已被移动。
  • file目录中是否有目录file?这些文件应该放在哪里?
  • 目录中是否有海量文件file?这可能会扰乱*glob 扩展。
  • 文件名是否与file目录外的文件发生冲突?是否应覆盖这些文件?
  • 文件名中有空格吗?这会弄乱一切。

为了修复您的find命令,我会很乐意忽略所有这些因素。

-execdir以下是使用和sh -c延迟扩展的修复方法*

find ./localFolders -name file -type d -execdir sh -c 'mv {}/* ..' \;

由于*位于引号中,因此不会提前展开。-execdir执行命令时,将在工作目录中sh -c展开。*

这是另一个没有的修复方法sh -c

find ./localFolders -path "*/file/*" -execdir mv {} .. \;

我过去常常-path查找名为 的目录下的所有项目file*用引号引起来,这样它们就不会提前展开。 这次find负责解释*。 请注意,这也会匹配类似 的内容./localFolders/foodir/file/bardir/file/somefile。 正如所说,我很高兴忽略了这个因素。

为了完整性,还有另一种解决方法-execdir

find ./localFolders -name file -type d -exec sh -c 'mv {}/* {}/..' \;

相关内容