我有嵌套的 pdf 文件目录,我想将它们提取到更高级别的目录并重命名如下:
我的文件类似于:
./path1/pathA/fileI.pdf
./path1/pathB/fileII.pdf
我想要实现:
./path1_pathA_fileI.pdf
./path1_pathB_fileII.pdf
我知道我可以通过以下方式列出文件列表
find . -type f -name "*.pdf"
我可以想象一个解决方案
find . -type f -name "*.pdf" | mv -t ...
但我不知道如何填写 ... 因为我不了解 bash 中的解析和变量赋值。如何在 '/' 处拆分路径并形成新的路径和文件名,如上所示?
提前谢谢了!
答案1
尝试:
find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'f=${1#./}; mv "$1" "./${f//\//_}"' None {} \;
对于所有文件名来说,这都是安全的,即使文件名中带有换行符。
怎么运行的
-mindepth 2
这告诉 find 不要处理当前目录中已有的任何文件。
-type f -name '*.pdf'
这会将搜索限制为具有
pdf
扩展名的常规文件。-exec bash -c '...' None {} \;
这将运行引号字符串中的命令,并提供文件名作为第一个参数
$1
。就我们的目的而言,字符串
None
只是一个占位符。$0
根据 bash 约定,它被分配给 ,是我们正在运行的命令的名称。f=${1#./}; mv "$1" "./${f//\//_}"
./
这将 (a)从文件名中删除前缀,以及 (b) 使用新名称将文件移动到所需位置。${1#./}
是 bash 的一个例子前缀删除$1
。它返回从./
开头删除的字符串。${f//\//_}
是 bash 的一个例子模式替换$f
。它返回所有/
替换为的字符串_
。要了解有关这些功能的更多信息,请参阅man bash
题为参数扩展。
更高效的版本
上面的版本对找到的每个文件都调用 bash。或者,我们可以对找到的几个文件只调用一次 bash。为此,我们将命令包装在一个for
循环中:
find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'for f in "$@"; do f=${f#./}; echo mv "$f" "./${f//\//_}"; done' None {} +
替代问题
假设我们想要的所有文件都在二级目录中,并且我们希望移动的文件的目录名称顺序反转,这样./path1_pathA_fileI.pdf
我们最终得到的不是 ,而是./pathA_path1_fileI.pdf
。在这种情况下:
for d1 in */; do d1=${d1%/}; for d2 in "$d1"/*/; do d2=${d2%/}; p="${d2#$d1/}_$d1"; for f in "./$d2"/*.pdf; do echo mv "$f" "./${p}_${f#./$d2/}"; done; done; done
或者,对于那些喜欢将命令分散到多行的人来说:
for d1 in */
do
d1=${d1%/}
for d2 in "$d1"/*/
do
d2=${d2%/}
p="${d2#$d1/}_$d1"
for f in "./$d2"/*.pdf
do
echo mv "$f" "./${p}_${f#./$d2/}"
done
done
done