我正在尝试编写一个脚本,该脚本将在目录中搜索文件夹,并将文件夹内容从其位置向上移动一个目录。这是我目前所拥有的:
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
你必须了解以下概念:
- 进程的工作目录。
- bash(或其他 shell)的 glob 特性。
Linux 中的每个进程都有一个工作目录作为其内部状态的一部分。您可以将其想象为文件系统中进程的“位置”。shell 也是一个进程,也有一个工作目录。shell 的工作目录是(通常)在您的提示符中显示的目录。
当一个进程被创建时,它将从其父进程继承工作目录。如果你从 shell 启动一个进程,那么 shell 就是父进程。每个进程都可以随意更改自己的工作目录。你可以使用内置命令更改 shell 的工作目录cd
。
处理相对文件名时会用到工作目录。相对文件名是不以 开头的文件名/
。例如,当您执行 时,它会在当前工作目录中cat foo
查找文件,但当您执行 时,它会在 中查找文件。foo
cat /tmp/foo
foo
/tmp
处理时,find -exec
你总是要考虑工作目录。 的工作目录-exec
是 的工作目录find
。 的工作目录find
是 shell 的工作目录。
就你的情况而言:
find ./localFolders -name 'file' -type d -exec mv {}/* .. \;
执行命令时,将引用工作目录的父目录,该工作目录是 shell 的工作目录。这不是您..
想要的mv
。-exec
find
有一个-execdir
类似的-exec
,但它首先将工作目录切换到找到匹配项的目录。-execdir
是解决您的问题的一大步,但您的find
命令中还有另一个问题:*
又名 glob。
glob 类似于通配符。例如,foo*
将匹配foobar
和foobaz
(并且foo
因为*
也匹配空字符串)。到目前为止一切顺利。globbing 的有趣之处在于它是由 shell 而不是执行的命令完成的。当您执行时,rm foo*
shell 将首先将扩展为*
和foobar
,foobaz
然后执行rm foobar foobaz
。rm
永远不会看到*
。
如果当前目录中没有以 开头的文件,会发生什么情况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*
通常不会扩展到以点开头的文件名。 - 目录中还有其他目录吗
file
?find
将尝试递归到它们但会失败,因为它们已被移动。 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 {}/* {}/..' \;