我遇到了需要以以下形式重命名大量文件的情况:
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 find
with-print0
和 GNU sort
with-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.csv
file_1.csv
在替换中,$1
, $2
... 包含与每一对 , 相匹配的内容(
,其)
模式类似于 in的运算符。perl
s///
是-f
用来禁用 的zmv
健全性检查的,例如,它会抱怨file6.csv
被重命名为已经存在的file7.csv
(即使到那时,file7.csv
已经被重命名为file8.csv
)。
您可以使用 perlrename
进行重命名,但您仍然需要像zsh
glob 这样的东西以正确的顺序传递列表:
$ (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
如果担心命令行参数被破坏,那么您可以在函数内执行所有这些操作并调用该函数。