【问题标题】:Sequentially numbering of files in different folders while keeping the name after the number按顺序对不同文件夹中的文件进行编号,同时在编号后保留名称
【发布时间】:2020-07-02 10:19:48
【问题描述】:

我在不同的文件夹中有很多 ogg 或 wave 文件,我想按顺序编号,同时保留前缀编号后面的所有内容。输入可能如下所示

Folder1/01 Insbruck.ogg
        02 From Milan to Rome.ogg
        03 From Rome to Naples.ogg


Folder2/01 From Naples to Palermo.ogg
        02 From Palermo to Syracrus.ogg
        03 From Syracrus to Tropea

输出应该是:

Folder1/01 Insbruck.ogg
        02 From Milan to Rome.ogg
        03 From Rome to Naples.ogg

Folder2/04 From Naples to Palermo.ogg
        05 From Palermo to Syracrus.ogg
        06 From Syracrus to Tropea.ogg

跨文件夹的顺序编号可以使用我找到的这个 BASH 脚本来完成 here:

find .  | (i=0; while read f; do 
let i+=1; mv "$f" "${f%/*}/$(printf %04d "$i").${f##*.}"; 
done)

但是这个脚本删除了我想保留的标题。

【问题讨论】:

标签: bash rename


【解决方案1】:

TL;DR

像这样,使用和perl rename

rename -n 's@/\d+@sprintf "/%0.2d", ++$::c@e' Folder*/*

如果输出看起来不错,请放弃 -n 开关。

使用-n,您只能看到真正要重命名的文件,因此只有来自Folder2 的3 个文件。


走得更远

变量$::c(或$main::c是一个包变量)是一个小技巧以避免使用更复杂的表达式:

rename -n 's@/\d+@sprintf "/%0.2d", ++our $c@e' Folder*/*

rename -n '{ no strict; s@/\d+@sprintf "/%0.2d", ++$c@e; }' Folder*/*

rename -n '
    do {
        use 5.012;
        state $c = 0;
        s@/\d+@sprintf "/%0.2d", ++$c@e
    }
' Folder*/*

感谢 freenode 上的 go|dfish 和 Grinnz

【讨论】:

  • 我测试了你第一次重命名一个没有导出 c=0 的衬里,效果很好。好消息是这两种解决方案都对文件夹敏感:所以我可以在不同的文件夹名称上运行脚本:这非常有用,因为文件夹的名称包含我要按顺序编号的作品的名称。但可能有不止一项工作 - 让我们说“Hamlet1 ... 4”和“Othello ..1..4”,这样我可以单独运行脚本。非常感谢。
  • 没有export 是正常的,因为c 是在perl 中创建的。我修改答案。感谢您通过单击旁边的大复选标记 (✓) 来接受它是否满足您的需求,并且还可以选择对其进行投票(投票至少需要 15 个声望点)。如果您发现其他答案有帮助,请给他们投票。接受和投票有助于未来的读者。请看the relevant help-center article
【解决方案2】:

这个工作的 bash 脚本是:

#!/bin/bash

argc=$#
width=${#argc}

n=0
for src; do
    base=$(basename "$src")
    dir=$(dirname "$src")
    if ! [[ $base =~ ^[0-9]+\ .*\.(ogg|wav)$ ]]; then
        echo "$src: Unexpected file name. Skipping..." >&2
        continue
    fi
    printf -v dest "$dir/%0${width}d ${base#* }" $((++n))
    echo "moving '$src' to '$dest'"
#   mv -n "$src" "$dest"
done

并且可以运行为

./renum Folder*/*

假设脚本保存为renum。它只会打印出源文件名和目标文件名。要进行实际移动,您应该在确保它按预期工作后将# 放在# mv -n "$src" "$dest" 行的开头。请注意,由于-n 选项,mv 命令不会覆盖现有文件。这可能是可取的,也可能不是可取的。该脚本将打印出警告消息并跳过意外的文件名,即文件名不符合问题中指定的模式。

【讨论】:

  • 这个脚本版本也很方便:不过有一件事:是否可以根据文件数量动态调整“%04d”参数?那么小于 10000 是 "%04d" 小于 1000 是 "%03d" 小于 100 是 "%02d" 吗?
  • @PeterStrawson 是的,我为此编辑了脚本。 argc 是参数计数,widthargc 中的小数位数。但是,如果参数列表中存在不符合模式的文件名(会在脚本中发出警告消息的文件名),这种简单的方法可能会失败。该脚本也可能出现“参数列表太长”错误。
【解决方案3】:

可以使用我在这里找到的这个 BASH 脚本来完成跨文件夹的顺序编号:

find .  | (i=0; while read f; do 
let i+=1; mv "$f" "${f%/*}/$(printf %04d "$i").${f##*.}"; 
done)

但是这个脚本删除了我想保留的标题。


不像公认的答案那么可靠,但这是脚本的改进版本,以防万一rename 不可用。

#!/usr/bin/env bash

[[ -n $1 ]] || {
  printf >&2 'Needs a directory as an argument!\n'
  exit 1
}

n=1
directory=("$@")

while IFS= read -r files; do
  if [[ $files =~ ^(.+)?\/([[:digit:]]+[^[:blank:]]+)(.+)$ ]]; then
    printf -v int '%02d' "$((n++))"
    [[ -e "${BASH_REMATCH[1]}/$int${BASH_REMATCH[3]}" ]] && {
      printf '%s is already in sequential order, skipping!\n' "$files"
      continue
    } 
    echo mv -v "$files" "${BASH_REMATCH[1]}/$int${BASH_REMATCH[3]}"
  fi  
done < <(find "${directory[@]}" -type f | sort )

现在以有问题的目录作为参数运行脚本。

./myscript Folder*/

./myscript Folder1/

./myscript Folder2/

或者..是当前目录。

./myscript .

等等……

  • 如果您对输出满意,请删除 echo

【讨论】:

  • 但这似乎包含脚本中文件夹的名称。这意味着需要为每次使用更改脚本。 M. Nejat Aydin 解决方案允许在执行脚本时调用文件夹名称。还是我在这里遗漏了什么?
  • 感谢您的精彩评论,我已更新答案以提供文件所在的目录/路径作为参数。
猜你喜欢
  • 1970-01-01
  • 2020-08-31
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-11
  • 1970-01-01
相关资源
最近更新 更多