根据文件的父目录重命名文件

根据文件的父目录重命名文件

我有一个主目录,比如说main.该主目录有一些文件夹,其中有三个名为ccc ddd和的文件夹lll,这是我想要定位和处理的唯一文件夹。每个子文件夹中都有一些文件夹,这些文件夹在三个子文件夹中的名称都相同,因此ccc ddd lll子文件夹中包含相同名称的文件夹。

然后,其中的每个子子文件夹ccc ddd lll都有许多以特定名称命名的文件,例如 this c_000 d_000 l_000c_001 d_001 l_001等等。

我首先要做的是重命名这些文件,以便它们的两个父目录将附加在文件名的开头,如下所示ccc_foo1_c_000 ddd_foo1_d_000 lll_foo1_l_000。我问是否有人可以建议我如何在脚本中完成此操作.sh

这是我的主文件夹的简单树形结构:

.
`-- main
    |-- ccc
    |   `-- foO1
    |   |    |-- c_000
    |   |    `-- c_001
    |   |
         -- foo2
    |       |-- c_000
    |       `-- c_001
    |   
    |   
    |-- ddd
    |   `-- foO1
    |   |    |-- d_000
    |   |    `-- d_001
    |   |
         -- foo2
    |       |-- d_000
    |       `-- d_001
    |-- lll
    |      `-- foO1
    |  |    |-- l_000
    |  |    `-- l_001
    |  |
    |   -- foo2
    |      |-- l_000
    |      `-- l_001
    |
    |-- aaa
    |-- bbb

答案1

你可以这样做:

for dir in ccc ddd lll; do 
    find "main/$dir" -type f -print0 | 
        while IFS= read -r -d '' f; do 
            dd=$(dirname "$f")
            new="${f/main\/}"
            new="${new//\//_}" 
            mv "$f" "$dd"/"$new"
        done
done

完成上述脚本后,您的文件将如下所示:

.
`-- main
    |-- ccc
    |   |-- fo01
    |   |   |-- ccc_fo01_c_000
    |   |   `-- ccc_fo01_c_001
    |   `-- fo02
    |       |-- ccc_fo02_c_000
    |       `-- ccc_fo02_c_001
    |-- ddd
    |   |-- fo01
    |   |   |-- ddd_fo01_d_000
    |   |   `-- ddd_fo01_d_001
    |   `-- fo02
    |       |-- ddd_fo02_d_000
    |       `-- ddd_fo02_d_001
    `-- lll
        |-- fo01
        |   |-- lll_fo01_l_000
        |   `-- lll_fo01_l_001
        `-- fo02
            |-- lll_fo02_l_000
            `-- lll_fo02_l_001

解释

  • for dir in ccc ddd lll; do ...; done:仅对这三个目录名称执行此操作,将它们分别保存为$dir.
  • find "main/$dir" -type f -print0 |:查找type f中的所有文件 ( ) main/$dir,并以空字符串 ( -print0) 分隔打印它们,以确保即使文件/目录名称包含换行符也能正常工作。
  • while IFS= read -r -d '' f; do:这将迭代结果find,将每个结果保存为$f.需要IFS=避免打破空白,这样-r反斜杠就不会被特殊处理,并且-d ''允许读取空分隔的输入。
  • dd=$(dirname "$f"):$dd现在是当前文件所在的目录。例如,main/ccc/fo01
  • new="${f/main\/}":这是使用外壳的字符串操作main/从文件路径中删除字符串的能力。
  • new="${new//\//_}":如上所述,现在我们将所有内容替换/为下划线。这会产生类似 的字符串ccc_fo01_c000
  • mv "$f" "$dd"/"$new":最后,我们将原始文件重命名$f$dd/$new.请记住,这$dd是该文件所在的目录,并且$new现在是您要将其重命名为的名称。

我建议您在实际执行此操作之前在测试echo之前添加一个mv

答案2

这是一个简单的 POSIX shell 解决方案,它循环文件、解析名称的目录部分并逐个重命名它们。

for x in ccc/*/* ddd/*/* lll/*/*; do
  dir2=${x%/*}; dir1=${dir2%/*}; dir2=${dir2#"$dir1"}
  mv -- "$x" "${x%/*}/${dir1}_${dir2}_${x##*/}"
done

在 bash 中,您可以使用参数扩展字符串替换功能以避免解析文件名。

for x in ccc/*/* ddd/*/* lll/*/*; do
  mv -- "$x" "${x%/*}/${x//\//_}"
done

在 zsh 中,您可以使用zmv根据模式重命名文件。

autoload -U zmv
zmv '(ccc|ddd|lll)/(*)/(*)' '$1/$2/${1}_${2}_$3'

答案3

对于从目录中运行的软编码解决方案main

#!/bin/bash

for f in $(find . -name '*_00*'); do
    mv "$f" "$(dirname "$f")/$(dirname "${f:2}" | tr "/" "_")_$(basename $f)"
done

这应该递归地遍历所有目录,查找包含该模式的所有文件*_00*。每个文件均使用由路径 ( $(dirname $"f"))、包含下划线和删除的前两个字符的转换路径 ( $(dirname "${f:2}" | tr "/" "_")) 以及原始文件名本身 ( $(basename $f)) 组成的组合单独重命名。

相关内容