按类型查找文件并根据其父目录重命名它们

Finding files by type and renaming them based on their parent directory

所以我尽我所能仔细地浏览了互联网,试图找到一些东西来帮助我解决我目前遇到的问题。

例如,我有一个包含许多目录的文件,其中包含文档和图像。

我的目标是将这些文件重命名为基于它们的父文件夹,例如:

/main/secondary/file

因为我所有的文件都已经通用命名,我希望能够将我的图像重命名为 secondary0001.jpg secondary0002.jpg 等等。

我一直在寻找并尝试使用各种方法来创建一个有效的脚本。

目前我觉得这可能是我迄今为止最好的努力。

find  -type f -iname IMG_[0-9][0-9][0-9][0-9].jpg -exec mv -n {}$dirname {}.jpg\; 

$2 包含我的整个文件夹的文件夹,因此 $2 等同于 Alpha/Primary/Secondary/file

非常感谢任何形式的帮助,谢谢。

假设您的图像文件名不包含白色 space 并且您的文件夹名称不包含白色 space(因此不需要极端的滑稽动作来处理极其尴尬的文件名), 那么可以考虑:

find "$directory" -type f -iname 'IMG_[0-9][0-9][0-9][0-9].jpg' -print |
while read file
do
    base=$(basename "$file")
    dir=$(dirname "$file")
    bdir=$(basename "$dir")
    suffix=$(echo "$base" | sed 's/^[Ii][Mm][Gg]_//')
    mv "$file" "$dir/$bdir$suffix"
done

我没说什么高效。因为你没有用 Bash 或 Ksh 标记它,所以我没有假设它们有任何变量编辑功能。除了使用 $(…) 代替 back-ticks `…`-iname 选项 find 之外,这基本上适用于任何 shell 派生来自 Bourne shell 在过去 20 年左右的时间里。

如果您决定在您的目录或文件名中需要 spaces 等,您将需要检查代码。它可能大部分是安全的(因为它在变量引用周围使用双引号,如 "$file"),但如果您的文件名或目录名可以包含换行符,您真的需要担心。


Using your method I've now got around to a method of renaming these files. However, when I rename them based on their directory, I'm writing over each file and losing many. Is there a way to avoid this such as adding digits to the end of the filename?

  1. 通过在 mv 前面放置一个 echo 进行测试,这样您就知道会发生什么,而无需实际发生。
  2. 我想你一定是修改了代码,或者情况与合理推断的略有不同。下面是一个示例,在全新的垃圾目录层次结构中包含一组空文件。每个目录的输入名称是唯一的;每个目录的输出名称是唯一的;除非目录中已经存在使用修改后的命名方案的文件,否则脚本无法生成冲突和丢失数据。即使您将文件上移一个级别,名称也应该是唯一的,因为首先子目录是唯一的。

示例运行:

$ mkdir junk
$ cd junk
$ for dir in primary secondary tertiary
> do (mkdir $dir; cd $dir; touch $(seq -f 'IMG_%04.0f.jpg' 1 10))
> done
$ ls
primary   secondary tertiary
$ ls *
primary:
IMG_0001.jpg IMG_0002.jpg IMG_0003.jpg IMG_0004.jpg IMG_0005.jpg IMG_0006.jpg IMG_0007.jpg IMG_0008.jpg IMG_0009.jpg IMG_0010.jpg

secondary:
IMG_0001.jpg IMG_0002.jpg IMG_0003.jpg IMG_0004.jpg IMG_0005.jpg IMG_0006.jpg IMG_0007.jpg IMG_0008.jpg IMG_0009.jpg IMG_0010.jpg

tertiary:
IMG_0001.jpg IMG_0002.jpg IMG_0003.jpg IMG_0004.jpg IMG_0005.jpg IMG_0006.jpg IMG_0007.jpg IMG_0008.jpg IMG_0009.jpg IMG_0010.jpg
$ directory=.
$ find "$directory" -type f -iname 'IMG_[0-9][0-9][0-9][0-9].jpg' -print |
> while read file
> do
>     base=$(basename "$file")
>     dir=$(dirname "$file")
>     bdir=$(basename "$dir")
>     suffix=$(echo "$base" | sed 's/^[Ii][Mm][Gg]_//')
>     mv "$file" "$dir/$bdir$suffix"
> done
$ ls
primary   secondary tertiary
$ ls *
primary:
primary0001.jpg primary0003.jpg primary0005.jpg primary0007.jpg primary0009.jpg
primary0002.jpg primary0004.jpg primary0006.jpg primary0008.jpg primary0010.jpg

secondary:
secondary0001.jpg secondary0003.jpg secondary0005.jpg secondary0007.jpg secondary0009.jpg
secondary0002.jpg secondary0004.jpg secondary0006.jpg secondary0008.jpg secondary0010.jpg

tertiary:
tertiary0001.jpg tertiary0003.jpg tertiary0005.jpg tertiary0007.jpg tertiary0009.jpg
tertiary0002.jpg tertiary0004.jpg tertiary0006.jpg tertiary0008.jpg tertiary0010.jpg
$

当我在每个目录中创建 1000 个文件并计时移动时,重命名 3000 个文件需要 46 秒(运行ning on Mac OS X 10.10.4 with一个硬盘,没有 SSD)。这比我预期的要长一些。

如下所示修改脚本将每个目录 1000 个文件的 运行时间减少到 8 秒(从 46 秒),速度提高了大约 5 秒。这是一个值得改进的地方,但感觉仍然该脚本没有 运行ning 像现代 Linux 那样快 — 但这可能是古老机器、硬盘、HTFS 文件系统和 Mac OS 的组合X 开销(window 的标题栏更改了当前的 运行ning 命令名称,例如脚本是 运行ning)。

directory='.'
time find "$directory" -type f -iname 'IMG_[0-9][0-9][0-9][0-9].jpg' -print |
while read file
do
    #base=$(basename "$file")
    base=${file##*/}
    #dir=$(dirname "$file")
    dir=${file%/*}
    #bdir=$(basename "$dir")
    bdir=${dir#*/}
    #suffix=$(echo "$base" | sed 's/^[Ii][Mm][Gg]_//')
    suffix=${base/[Ii][Mm][Gg]_/}
    mv "$file" "$dir/$bdir$suffix"
done

为了进一步改进,我会使用 Perl 并让它以系统调用的形式执行重命名操作,而不是调用单独的程序。这将减少更多的进程开销(修改后的脚本中仍有 3000 个 mv 命令,而 Perl 或等效程序在整个移动过程中只有一个进程)。

请注意,参数替换起作用是因为名称被限制为 well-behaved(每个名称中至少有一个斜线;根目录未命名,等等)。 basenamedirname 命令处理的边界情况不由参数替换处理。谨慎泛化。