Bash通过解析find的输出来移动和重命名


3

我有嵌套的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中的解析和变量赋值。如何在“/”处拆分路径并形成如上所述的新路径和文件名?

提前谢谢了!

Answers:


6

尝试:

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的前缀删除的一个例子。它返回strign $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

感谢您一步一步细分!如果我还想更改两个dir字符串的顺序以便最终路径为./pathA_path1_fileI.pdf,那么模式替换$ {f // \ _ // _}不起作用。怎么可以这样做?
Canaryyellow 2016年

@Canaryyellow好的。我为此添加了代码。
约翰1024年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.