对于每个子文件夹,按名称对文件进行排序并将其重命名为连续的填充数字(无论扩展名如何)

对于每个子文件夹,按名称对文件进行排序并将其重命名为连续的填充数字(无论扩展名如何)

我有以下目录结构:

.
├── event-a
│   ├── album-a
│   │   ├── a.jpg
│   │   └── x.png
│   └── album-b
│       ├── a.jpg
│       ├── x.png
│       └── y.gif
└── event-b
    ├── album-x
    │   ├── a.jpg
    │   └── x.png
    └── album-y
        ├── a.jpg
        ├── x.png
        └── y.gif

对于每个二级子文件夹(album-foo在示例中命名),我想按名称对文件进行排序,并将它们重命名为连续的填充数字,无论其扩展名如何。这些文件夹可能包含 JPG、PNG 或 GIF 图像,并且应保留所有文件扩展名。

所以,在这种情况下,我想得到这样的结果:

.
├── event-a
│   ├── album-a
│   │   ├── 01.jpg
│   │   └── 02.png
│   └── album-b
│       ├── 01.jpg
│       ├── 02.png
│       └── 03.gif
└── event-b
    ├── album-x
    │   ├── 01.jpg
    │   └── 02.png
    └── album-y
        ├── 01.jpg
        ├── 02.png
        └── 03.gif

实用性rename如果这能让事情变得更容易的话,可以使用。

主要问题是弄清楚每个文件应该获得的数量。我是否需要使用嵌套for循环来迭代event-xalbum-x子文件夹,然后对于每个内部循环,自己跟踪数字,或者是否有一些我缺少的聪明的解决方案?

答案1

for dir in */*; do           # loop over the directories
    (                        # run in a subshell ...
        cd "$dir"            # ... so we don't have to cd back
        files=(*)            # store the filenames in a zero-indexed array

        for index in "${!files[@]}"; do
            file=${files[$index]}
            ext=${file##*.}
            newname=$(printf "%02d.%s" $((index+1)) "$ext")
            mv "$file" "$newname"
        done
    )
done

假设您有一个没有扩展名的文件。在这种情况下,除了前导数字之外,它将具有相同的名称(例如my_file=> 05.my_file

所有非隐藏目录条目都将被重命名,包括目录。

答案2

您这里有两组问题:

  1. 递归到目录中。
  2. 在每个单独的目录中重新启动编号系统。

这将顺利处理这两种情况,但如果您决定还包含其他扩展,则需要修改 IMAGE_TYPES 变量。

#!/bin/bash

shopt -s extglob
shopt -s nocaseglob

IMAGE_TYPES='jpg|png|gif'
IFS=$'\n' dirlist=(`find "$PWD" -type d`)

for dir in "${dirlist[@]}"; do
    cd "$dir"
    ls *.+($IMAGE_TYPES) > /dev/null 2>&1 || continue

    counter=0
    for file in *.+($IMAGE_TYPES); do
        printf -v newname "%.3d.%s" $((counter += 1)) "${file##*.}"
        mv --verbose "$file" "$newname"
    done
done

答案3

我创建了同一棵树并进行了您的移动构建。这是我想出的:

n=0 IFS=.; set -f ./[e]vent*/*/*
for f do [ -n "${f##./\[*}" ] || break
    [ "$d" = "${f%/*}" ] || i=0 d=${f%/*}
    mv=': mv "${'$((n=$n+1))'}" "${'$n'%%/*}/'
    printf "$mv%.2d%.0s.%s\"\n" $((i=$i+1)) ${f##*/}
done | sh -sx -- "$@"

如果没有最后一个|管道,那么它会打印出以下内容:

mv "${1}" "${1%/*}/01.jpg"
mv "${2}" "${2%/*}/02.png"
mv "${3}" "${3%/*}/01.jpg"
mv "${4}" "${4%/*}/02.png"
mv "${5}" "${5%/*}/03.gif"
mv "${6}" "${6%/*}/01.jpg"
mv "${7}" "${7%/*}/02.png"
mv "${8}" "${8%/*}/01.jpg"
mv "${9}" "${9%/*}/02.png"
mv "${10}" "${10%/*}/03.gif"

我知道,它看起来并不多,但这里重要的是您不必担心任何奇怪的文件名或任何其他内容 - 该输出的预期目的地是与循环共享位置数组的forshell产生它的。我在命令中有一个这样的命令 - 它被配置为提供调试输出,如下所示:

+ : mv ./event-a/album-a/a.jpg ./event-a/album-a/01.jpg
+ : mv ./event-a/album-a/x.png ./event-a/album-a/02.png
+ : mv ./event-a/album-b/a.jpg ./event-a/album-b/01.jpg
+ : mv ./event-a/album-b/x.png ./event-a/album-b/02.png
+ : mv ./event-a/album-b/y.gif ./event-a/album-b/03.gif
+ : mv ./event-b/album-x/a.jpg ./event-b/album-x/01.jpg
+ : mv ./event-b/album-x/x.png ./event-b/album-x/02.png
+ : mv ./event-b/album-y/a.jpg ./event-b/album-y/01.jpg
+ : mv ./event-b/album-y/x.png ./event-b/album-y/02.png
+ : mv ./event-b/album-y/y.gif ./event-b/album-y/03.gif

:这就是当是格式字符串中的第一个字符时的样子printf。当我删除它并执行命令时:

ls ./event*/*/

./event-a/album-a/:
01.jpg  02.png

./event-a/album-b/:
01.jpg  02.png  03.gif

./event-b/album-x/:
01.jpg  02.png

./event-b/album-y/:
01.jpg  02.png  03.gif

这似乎符合您的要求。我想你无法打败结果。

答案4

如果您知道文件的名称,则可以通过运行以下命令获取它的编号:

ls -1 <your_dir> | grep <file_name> -B 1000 | wc -l

相关内容