我有一个包含许多(> 5000)文件夹的目录:
folder1
folder2
folder3
...
所有这些文件夹都有许多子目录。在每个文件夹的特定子目录中,可能有一个tar.gz
存档。如果一个文件夹包含一个tar.gz
存档,则它只有一个,并且位于特定的子目录中。
例如:
folder1/foo/baz.tar.gz
folder2/bar/qux.tar.gz
folder3 [no tar.gz file in this folder]
...
我需要编写一个 bash 脚本来完成以下任务:
- 我想循环遍历每个文件夹,找到
tar.gz
存档(如果存在),并将其内容解压到不同的目录中(对于所有找到的存档都是相同的)。 - 找到每个
tar.gz
存档后,我还需要在与tar.gz
存档相同的目录中移动更多文件,可能需要存储存档的路径。
我可以列出所有档案:
find . -name "*tar.gz"
我想知道操作获得的命令是否是最佳解决方案,或者循环遍历每个目录会更好......
最优化的方法是什么?应该如何进行呢?
答案1
find
您基本上可以在的选项中执行任何单个操作或操作列表-exec
,那么为什么不untar
直接在 中执行每个存档呢find -exec
?对于复杂的命令,通常使用该-exec
功能来调用 shell,并使用 shell 命令的-c
选项将要运行的实际命令传递给它。例如(实际上有一种更简单的方法来执行这个实际示例,但它是为了展示想法):
-exec sh -c 'mv "$1" "~/$1"' sh {} ';'
这将为找到的每个文件启动一个 shell,并将该文件移动到您的$HOME
目录。请注意,它{}
用于将找到的文件名作为 shell 位置参数传递,即。$1
,因此在 shell 命令中$1
使用的是 ,而不是{}
。在您的情况下,这种类型的解决方案类似于:
-exec sh -c 'tar xvf "$1" -C "$(dirname $1)"' sh {} ';'
这个想法是这个习惯用法带来了您最喜欢的 shell 的所有功能之内命令find
。 (是的,您可以使用bash
或zsh
代替sh
,只是要注意sh
加载速度要快得多,并且当您处理许多文件时,这可能会增加)。
如果这是您重复执行的操作,或者您事先预计操作会花费很长时间,和如果你有一个多核 cpu,那么考虑第二个选项可能对你有利 - 将你的文件列表通过 GNU 管道传输parallel
,并让它untar
在所有核心上同时执行操作。初学者可以试试这个:
find . -name "*tar.gz" -type f -print0 |
parallel -0 tar xvf {} -C {//}
作为额外的效率,上述答案不再需要使用外部dirname
命令,因为 GNU 并行本身可以更有效地完成它。这就是他们{//}
正在做的事情。
警告:我不是这方面的专家,parallel
并且在没有实际使用经验的情况下提供该选项,因此如果其他人可以插话这是否是正确的方法,那就太好了。
答案2
您可以使用 find 命令将 tarball 路径传递给可以解压它们的脚本(我尚未对此进行测试):
$ cat script
#!/bin/bash --
tarball="${1}"
dir="$(dirname ${tarball})"
tar xvf "${tarball}" -C "${dir}"
然后使用 find 调用脚本:
$ find . -type f -name '*.tar.gz' -exec ./script "{}" \;
或者在一个 find 命令中(快速测试):
find . -type f -name '*.tar.gz' -exec sh -c 'dir="$(dirname ''"{}"'')"; tar xvf "{}" -C "${dir}"' \;