重新编号和重命名

重新编号和重命名

我遇到了需要以以下形式重命名大量文件的情况:

file.csv
file_1.csv
file_2.csv
file_3.csv
file_4.csv
file_5.csv
file_6.csv
file_7.csv
file_8.csv

为了更好地排序,即:

file_1.csv
file_2.csv
file_3.csv
file_4.csv
file_5.csv
file_6.csv
file_7.csv
file_8.csv
file_9.csv

即,我可以手动重命名第一个文件,但对于其余的“ _#”文件,我需要用+1.我尝试使用

rename -n -v 's/_(.*)./.(\1+1)./' 

但得到了

file_(1+1).csv
file_(2+1).csv

ETC。

请问有什么简单的方法可以批量保留“ _#”文件吗?

PS,更好的是

file_01.csv
. . .
file_09.csv
file_10.csv
. . .

如果可能的话。

更新:

感谢cas的回答。因为我将它们重命名为零填充名称,所以文件名不会发生冲突,所以对我来说命令可以简化为:

touch file.csv file_{1..9}.csv

$ rename -v 's/^(file_)(\d+)(\.csv)$/$1 . (sprintf "%02i", $2 + 1) . $3/e' file_*
file_1.csv renamed as file_02.csv
file_2.csv renamed as file_03.csv
file_3.csv renamed as file_04.csv
file_4.csv renamed as file_05.csv
file_5.csv renamed as file_06.csv
file_6.csv renamed as file_07.csv
file_7.csv renamed as file_08.csv
file_8.csv renamed as file_09.csv
file_9.csv renamed as file_10.csv

请注意最后两个文件名:

file_09.csv
file_10.csv

答案1

您需要使用 perl 的/e正则表达式修饰符来使其将操作的右侧计算s///为 perl 表达式。

您还需要对文件名进行排序撤销数字顺序,以便重命名编号最大的文件名编号较小的文件名(否则会出现文件名冲突 - 默认情况下,除非您使用-f强制它,rename否则将拒绝覆盖现有文件)。为此,我将使用 GNU findwith-print0和 GNU sortwith-z进行 NUL 终止输入,-r以及-V反向版本(即“自然”)排序。 -t _-k 2选项也用于从第二个字段排序。

rename 的-d选项用于使其仅重命名路径名的文件名部分,并-0使其在标准输入上采用 NUL 分隔的文件列表。

例如

$ touch file.csv file_{1..8}.csv
$ find . -name 'file_*.csv' -print0 |
    sort -z -t _ -k2 -r -V |
    rename -d -0 's/^(file_)(\d+)(\.csv)$/$1 . ($2 + 1) . $3/e'
$ mv file.csv file_1.csv
$ ls -l
total 5
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_1.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_2.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_3.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_4.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_5.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_6.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_7.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_8.csv
-rw-r--r-- 1 cas cas 0 Aug 21 15:22 file_9.csv

这可以稍微简化一下,但我已经让正则表达式显式查找并捕获file_、一个或多个数字以及.csv扩展名,以避免任何不应该重命名文件的可能性。

要使文件编号用零填充,可以使用该sprintf函数。例如

... | rename -0 -d -v 's/^(file_)(\d+)(\.csv)$/$1 . (sprintf "%02i", $2 + 1) . $3/e'
Reading filenames from file handle (GLOB(0x555555905960))
./file_8.csv renamed as ./file_09.csv
./file_7.csv renamed as ./file_08.csv
./file_6.csv renamed as ./file_07.csv
./file_5.csv renamed as ./file_06.csv
./file_4.csv renamed as ./file_05.csv
./file_3.csv renamed as ./file_04.csv
./file_2.csv renamed as ./file_03.csv
./file_1.csv renamed as ./file_02.csv

答案2

zsh

$ autoload -Uz zmv # best in ~/.zshrc
$ LC_ALL=C zmv -f -n '(file)(|_(<->))(.csv)(#qnOn)' '${1}_$(($3+1))$4'
mv -- file_11.csv file_12.csv
mv -- file_10.csv file_11.csv
mv -- file_9.csv file_10.csv
mv -- file_8.csv file_9.csv
mv -- file_7.csv file_8.csv
mv -- file_6.csv file_7.csv
mv -- file_5.csv file_6.csv
mv -- file_4.csv file_5.csv
mv -- file_3.csv file_4.csv
mv -- file_2.csv file_3.csv
mv -- file_1.csv file_2.csv
mv -- file.csv file_1.csv

或者使用0-padding 使用${(l[2][0])...}left-padding 参数扩展标志:

$ LC_ALL=C zmv -fn '(file)(|_(<->))(.csv)(#qnOn)' '${1}_${(l[2][0])$(($3+1))}$4'
mv -- file_11.csv file_12.csv
mv -- file_10.csv file_11.csv
mv -- file_9.csv file_10.csv
mv -- file_8.csv file_09.csv
mv -- file_7.csv file_08.csv
mv -- file_6.csv file_07.csv
mv -- file_5.csv file_06.csv
mv -- file_4.csv file_05.csv
mv -- file_3.csv file_04.csv
mv -- file_2.csv file_03.csv
mv -- file_1.csv file_02.csv
mv -- file.csv file_01.csv

(如果可以的话,删除-n(试运行))。

这会重命名由该 glob 生成的文件,extendedglob其中:

  • <->匹配任何数字序列(它是<x-y>与没有指定界限的运算符匹配的十进制整数)
  • (#q...)指定 glob 限定符,其中:
    • n:切换数字全局排序
    • On: 按名称反向排序。所以file8在之前file7,但也感谢上面的 numericglobsort,file10在之前file9。这LC_ALL=C是为了确保排序算法中不会忽略 和 ,因此_位于后面.file.csvfile_1.csv

在替换中,$1, $2... 包含与每一对 , 相匹配的内容(,其)模式类似于 in的运算符。perls///

-f用来禁用 的zmv健全性检查的,例如,它会抱怨file6.csv被重命名为已经存在的file7.csv(即使到那时,file7.csv已经被重命名为file8.csv)。

您可以使用 perlrename进行重命名,但您仍然需要像zshglob 这样的东西以正确的顺序传递列表:

$ (LC_ALL=C; rename -f -n '
    s{^(file)(?:_(\d*))?(\.csv)\Z}{
      "${1}_" . (($2 // 0) + 1) . $3}se
   ' file(|_<->).csv(nOn))
rename(file_11.csv, file_12.csv)
rename(file_10.csv, file_11.csv)
rename(file_9.csv, file_10.csv)
rename(file_7.csv, file_8.csv)
rename(file_6.csv, file_7.csv)
rename(file_5.csv, file_6.csv)
rename(file_4.csv, file_5.csv)
rename(file_3.csv, file_4.csv)
rename(file_2.csv, file_3.csv)
rename(file_1.csv, file_2.csv)
rename(file.csv, file_1.csv)

例如替换(($2 // 0) + 1)sprintf("%02d", ($2 // 0) + 1)进行 0 填充。

如果您没有并且无法安装zsh,但您使用的是 GNU 系统,则可以使用bash以下命令以正确的顺序获取这些文件:

eval "files=(
  $(
    shopt -s extglob failglob
    export LC_ALL=C
    ls --quoting-style=shell-always -rvd file?(_+([0-9])).csv
   )
)"

依靠 GNUls -rv以相反的版本顺序列出文件。

然后使用rename ... "${files[@]}"rename上面相同的代码进行调用以进行重命名。

在这里,由于文件的名称非常简单,并且假设没有其他文件与 匹配file*.csv,并且使用默认值$IFS,您也可以这样做(仍然使用 GNU ls 但使用任何类似 Bourne 的 shell):

rename ... $(LC_ALL=C ls -rvd file*.csv)

答案3

一种方法可以如下所示:

set -- file_*.csv

while [ "$#" -gt 0 ]; do
  set -- "$#" "$@"
  mv -- "file_$1.csv" "file_$#.csv"
  shift 2
done

mv file.csv file_1.csv

如果您想要保留零填充的文件名,请将mv语句更改为:

mv -- "file_$1.csv" "$(printf 'file_%02d.csv\n' "$#")"
mv file.csv file_01.csv

如果担心命令行参数被破坏,那么您可以在函数内执行所有这些操作并调用该函数。

相关内容