我对脚本编写(bash shell)非常陌生,我无法弄清楚我试图完成的这项任务的逻辑。
我有一个文件夹,其文件名如下(例如):
PIC-100
PIC-1
PIC-23
PIC-5
我希望能够从他们的文件名中获取数字,按从最低到最高的顺序对它们进行排名,然后根据它们在排名列表中的位置对其进行重命名。
例如,它们将被重命名为:
PIC-100 --> PIC-4
PIC-1 --> PIC-1
PIC-23 --> PIC-3
PIC-5 --> PIC-2
谢谢大家。
答案1
我将向您展示如何逐步解决这个问题。首先,让我们创建一些测试数据:
mkdir /tmp/blah && cd /tmp/blah
for f in 1 90 $RANDOM $RANDOM $RANDOM; do touch PIC-$f; done
ls
文件应按名称排序,但我们希望它们按数字排序。该命令sort
可以帮助解决这个问题:
ls | sort -V
-V
意味着“版本化排序”,这适合您的情况。对于其他选项,请参阅man sort
。
现在进行重命名。有很多方法可以做到这一点,但我将循环遍历排序列表的输出。我们需要一个变量和一个循环:
counter=1 ls PIC-* | sort -V | while read fname; do
echo mv -i "$fname" NEWPIC-$counter
let counter+=1
done
这里发生了很多事情,所以我将详细解释 - 从安全开始。首先,备份您的文件在继续之前 - 循环很容易出错并导致糟糕的一天。这是备份所有内容的命令:
mkdir backup; cp PIC* backup
如果这个循环出错,我们可能会弄得一团糟,所以我想在运行它之前看看会发生什么。注意echo
?这将打印出预期的命令而不是运行它们。您可以删除echo
或仅复制粘贴输出以“真正”运行命令
接下来,我就开始mv -i
争取更名。如果我要覆盖某些内容,这会提示 - 无论如何,那时一切都已经变成了梨形,但至少我会知道。
第三,请注意我将其重命名PIC-xxx
为NEWPIC-xxx
。这是真的很重要因为在某些情况下我可能会意外地将 PIC-3 重命名为 PIC-2(例如,如果由于某种原因存在 PIC-2.5)。我们可以NEW
稍后清理该部分。
另外,我用 启动命令counter=1 ls | sort -V | ...
。通过设置变量在命令,当我忘记在测试运行之间设置它时,我避免看到“奇怪”的行为 - 目标文件名将以NEWPIC-6
或其他内容开头,而不是NEWPIC-1
.这是你犯了几次错误后养成的习惯之一:)
现在来说说循环本身。
while read fname
说“每次看到一行输入时,将该行放入变量中fname
并运行下一部分do .. done
包围您要为每一行运行的代码echo mv -i "$fname" NEWPIC-$counter
打印出您要运行的命令let counter+=1
增加你的计数器变量。如果您忘记了这一点,您最终会调用一个文件NEWPIC-1
,而所有其他文件都会消失。这就是我们有备份的原因:)
最后,这里有一个小技巧可以NEW
从文件名的开头删除它:
for fname in NEWPIC-*; do echo mv $fname ${fname#NEW}; done
echo
和以前一样,当您对输出满意时删除。
答案2
这个带注释的 shell 脚本可以做你想要的事情。分号表示顺序执行命令,竖线表示管道,井号开始注释,“do-done”对是“while”中包含的循环
n=1 ; # initialize counter
ls | # list current working directory
sort -V | # sort (thanks to dwurf for the flag, but this isn't in POSIX standard yet)
while read f ; do
mv "$f" PIC-$n ; # rename file
n=$((n+1)) ; # counter increment.
done
答案3
使用zsh
外壳:
tmpdir=$(mktemp -d)
n=1
for name in PIC-*(.n); do
mv $name $tmpdir/PIC-$(( n++ ))
done
mv $tmpdir/PIC-* .
rmdir $tmpdir
这会将当前目录中名称匹配的所有常规文件移动+重命名PIC-*
到临时目录中。文件根据文件名中的数字按排序的数字顺序移动(这是glob 限定符n
中的(.n)
作用;.
仅选择常规文件)。对于每个文件,该文件被重命名为PIC-n
其中n
是一个计数器。
移动并重命名所有文件后,它们将移回当前目录,并删除临时目录。
使用临时目录作为暂存区是为了避免名称冲突。
如果有数千个文件,则mv
可能需要在循环中执行第二个文件,以避免“参数列表太长”错误:
# first loop as before, then...
for name in $tmpdir/PIC-*; do
mv $name .
done
rmdir $tmpdir
如果您在末尾使用cp
文件而不是文件,则以“相反顺序”执行操作还将在临时目录中为您提供文件的原始名称备份:mv
tmpdir=$(mktemp -d)
mv PIC-*(.) $tmpdir
n=1
for name in $tmpdir/PIC-*(n); do
# change "mv" to "cp" (and remove "rmdir" below)
# to leave a copy in the temporary directory
mv $name PIC-$(( n++ ))
done
rmdir $tmpdir