我可以使用什么重命名命令来删除目录中的当前文件名并将其替换为字母数字文件名?
比如gg0001
,,,gg0002
等等?我希望该命令能够处理具有任何类型扩展名的文件。我希望保留该扩展名。
rename
我更喜欢使用Perl 。
我已经尝试过这个命令,但它只是在文件名前面加上“gg”,而我希望替换原始文件名:
rename 's/^/gg_/' *
答案1
您实际上并不需要rename
为此命令,您可以直接在 shell 中执行此操作:
c=0; for i in *; do let c++; mv "$i" "gg_$(printf "%03d" $c)${i#"${i%.*}"}"; done
将printf "%03d" $c
打印$c
变量,并根据需要用 3 个前导 0 填充。let c++
计数器递增 ( ) $c
。提取${i#"${i%.*}"}
扩展名。更多相关内容这里。
我不会用rename
这个。我认为它不能进行计算。有效的 Perl 构造如s/.*/$c++/e
失败,我没有看到任何其他方法让它进行计算。这意味着您仍然需要在循环中运行它,并让其他东西为您增加计数器。就像是:
c=0;for i in *; do let c++; rename -n 's/.*\.(.*)/'$c'.$1/' "$i"; done
但与使用更简单的方法相比并没有什么优势mv
。
答案2
如果文件名中没有'
单引号,您可以这样做:
i=0; set --
for f in *
do set -- "$@" "$f" gg "$((i+=1))" "${f#"${f%.*}"}"
done
printf "mv '%s' '%s%03d%s'\n" "$@" | sh
这样,如果文件名包含 a .
,则它及其后面的任何内容都会被保留,否则该字段为空并且printf
不打印任何内容。
我想即使有是文件名中的单引号,可以这样做......
i=0; set --
for f in *
do set -- "$@" "$((i+=1))" gg "$i" "$i#\"\${$i%.*}\""
done
printf 'mv "${%d}" "%s%03d${%s}"\n' "$@" | sh -s -- *
...总体来说这可能更安全。
但你知道,经过思考之后,我开始怀疑两者都太费力了。我通过管道传输任一列表的唯一原因是避免检查零填充。我更喜欢在一个子 shell 中构建一个列表,并在必要时将其与write()
一个子 shell 一起流式传输到另一个子 shell。
但话虽如此,零填充也不会太难。我稍微研究了一下,得出了这个小小的数学陈述:
$((z/=(m*=((i+=1)<$m?1:10))/$m))
如果放置在迭代循环中,它将为每个项目增加一次,对于每个十的因子以指数回退方式$i
推进乘数,并对每个相同的项目进行什一税。这个想法是在开始循环之前获取保存可能达到的最高增量的值。这很简单:$m
$z
$z
$i
set -- *; max=$# z=1
until [ "${#z}" -gt "${#max}" ]; do : "$((z*=10))"; done
这是一种完全自动化的方式 - 它将自动填充到可能增加的最高数字。但是,老实说,$z
只需是以 1 开头、后面仅跟零的数字,因此:
set -- *; z=1000
...将填充到数千字段。无论如何,这是我在测试时使用的一些演示:
time dash <<\CMD
str=100000 z=1 m=1 i=0
until [ "${#z}" -gt "${#str}" ]; do : "$((z*=10))"; done
while : "$((z/=(m*=((i+=1)<$m?1:10))/$m))" &&
case "$i" in (*[2-8]*|*9*[10]*|*1*[19]*) continue;;
([019]*) set -- "$@" "z: $z m: $m i: $i" "${z#?}$i";;esac
do [ "$i" = "$str" ] && printf %s\\n "$@" && break; done
CMD
其中大部分是过滤输出,因为我想确保它能够继续处理大量项目,但我不想读取 100000 个结果。
输出
z: 100000 m: 10 i: 1
000001
z: 100000 m: 10 i: 9
000009
z: 10000 m: 100 i: 10
000010
z: 10000 m: 100 i: 99
000099
z: 1000 m: 1000 i: 100
000100
z: 1000 m: 1000 i: 999
000999
z: 100 m: 10000 i: 1000
001000
z: 100 m: 10000 i: 9999
009999
z: 10 m: 100000 i: 10000
010000
z: 10 m: 100000 i: 99999
099999
z: 1 m: 1000000 i: 100000
100000
dash <<<'' 0.32s user 0.00s system 99% cpu 0.320 total
$m
应该初始化为您希望开始删除前导零的第一阶段 - 我刚刚m=1
在上面做了。$z
如上所述,存储最高数量级。并$i
计算循环次数,因此i=0
可能是一个安全的选择。无论如何,剩下的事情就会发生。这个想法只是剥离引线1
并将$z
其固定在任何地方。
set -- $(seq 1000); m=1 i=0 z=10000
while [ "$#" -gt 0 ] && : "$((z/=(m*=((i+=1)<$m?1:10))/$m))"
do printf 'mv "%s" "lead_string_%s"\n' "$1" "${z#1}$i${1#"${1%.*}"}"
shift; done
我用printf
它只是为了展示它的样子,当然,重点是避免打印数据,而是将其作为准备好的分隔参数移交给当前 shell。无论如何,它看起来像:
mv "1" "lead_string_0001"
mv "2" "lead_string_0002"
mv "3" "lead_string_0003"
...
mv "9" "lead_string_0009"
mv "10" "lead_string_0010"
mv "11" "lead_string_0011"
...
mv "15" "lead_string_0015"
...
mv "96" "lead_string_0096"
...
mv "99" "lead_string_0099"
mv "100" "lead_string_0100"
mv "101" "lead_string_0101"
mv "102" "lead_string_0102"
...
mv "998" "lead_string_0998"
mv "999" "lead_string_0999"
mv "1000" "lead_string_1000"
for
无论您使用的是or循环,都没有区别while
。所以对于文件你可以这样做:
m=1 i=0 z=10000
for f in *; do : "$((z/=(m*=((i+=1)<$m?1:10))/$m))"
mv -- "$f" "lead_string_${z#1}$i${f#"${f%.*}"}"
done
答案3
这对我有用。 (但rename
这里使用的命令是 a perl rename
)。
ls -1 -c | xargs rename -n 's/.*/our $i; sprintf("gg%04d", $i++)/e'
但是,上面假设不存在名称中包含空格的文件。否则它会破裂。另外,解析ls
输出确实不是一个好主意。
但是,由于文件名中有空格时会中断,因此让我们尝试解决上述问题。
find -print0 | xargs -0 rename 's/.*/our $i; sprintf("gg%04d", $i++)/e'
上面的语法有效,但仍然存在问题。问题是当你有子目录时,它也会重命名子目录,这正是我们想要的。
为了解决这个问题,我们可以按照 @terdon 在他的评论中建议的方式重新表述我们的命令。最终修改后的命令如下所示:
find -maxdepth 1 -type f -print0 | xargs -0 rename 's/.*/our $i; sprintf("gg%04d", $i++)/e'
测试
我的文件夹中有以下文件。别给我重命名是我们不想重命名的子目录。
ls
Don't rename me file1 file with spaces hello
现在,我执行该命令并得到以下输出。
find -maxdepth 1 -type f -print0 | xargs -0 rename -n 's/.*/our $i; sprintf("gg%04d", $i++)/e'
./file1 renamed as gg0000
./hello renamed as gg0001
./file with spaces renamed as gg0002
-n
如果我们对最终输出感到满意,则可以删除该标志。
参考
答案4
只需在每次迭代时增加一个变量即可。由于rename
set use strict
,您需要使用全局变量$::i
(或使用no strict
,或使用 声明变量our
)。
rename 'our $i; s/.*/gg_$i/; ++$i' *
如果您想用前导零填充数字,请计算文件数。文件列表位于@ARGV
.
rename 'our $i; my $width = length(scalar(@ARGV)); s/.*/sprintf("gg_%0${width}d", $i++)/e' *
您可以测试计数器变量是否定义为仅在第一次迭代时执行代码。
rename 'our ($i, $width); if (!defined $width) {$width = length(scalar(@ARGV)); $i = 0;} s/.*/sprintf("gg_%0${width}d", $i++)/e' *
在上述所有内容中,如果您传递带有目录部分的文件名(即,如果您传递包含/
- 的文件名,这里没有发生*
),请更改s/.*/…/
为s/[^/]*\z/…/
.