问题描述
13B12363_1B1_0.png
13B12363_1B1_1.png
13B12363_1B1_2.png
13B12363_1B1_3.png
13B12363_1B1_4.png
13B12363_1B1_5.png
13B12363_1B1_6.png
13B12363_1B1_7.png
13B12363_1B1_8.png
13B12363_1B1_9.png
13B12363_1B1_10.png
[...]
13B12363_1B1_495.png
13B12363_1B1_496.png
13B12363_1B1_497.png
13B12363_1B1_498.png
13B12363_1B1_499.png
经过一些后处理,我删除了一些文件,我想更新订购号,并用新的位置替换实际的号。看着这个previous question,我最终会做类似的事情:
(1)ls -v | cat -n | while read n f; do mv -i $f ${f%%[0-9]+.png}_$n.png; done
但是,此命令不能识别“序号+ png”,而只是在文件名的末尾附加新编号。像13B12363_1B1_10.png_9.png
另一方面,如果我这样做:
(2)ls -v * | cat -n | while read n f; do mv $f ${f%.*}_$n.png; done
添加订购号没有问题。像13B12363_1B1_10_9.png
因此,对于(1),似乎我没有正确指定digit
,但找不到正确的语法。到目前为止,我尝试了[0-9]
,[0-9]+
,[[:digits:]]
和[[:digits:]]+
。哪个才是合适的?
此外,在(2)中,我想知道如何指定rename
(CentOS版本)以删除第二个和第三个下划线之间的数字。这里我不得不说我有一些文件名,例如20B12363_22_10_9.png
,所以我应该以某种方式指定第二和第三下划线。
解决方法
使用Bash内置的Basic Regex Engine和null
分隔的文件列表。
经过样品测试
#!/usr/bin/env bash
prename=$1
# Bash setting to return empty result if no match found
shopt -s nullglob
# Create a temporary directory to prevent file rename collisions
tmpdir=$(mktemp -d) || exit 1
# Add a trap to remove the temporary directory on EXIT
trap 'rmdir -- "$tmpdir"' EXIT
# Initialize file counter
n=0
# Generate null delimited list of files
printf -- %s\\0 "${prename}_"*'.png' |
# Sort the null delimited list on 3rd field numeric order with _ separator
sort --zero-terminated --field-separator=_ --key=3n |
# Iterate the null delimited list
while IFS= read -r -d '' f; do
# If Bash Regex match the file name AND
# file has a different sequence number
if [[ "$f" =~ (.*)_([0-9]+)\.png$ ]] && [[ ${BASH_REMATCH[2]} -ne $n ]]; then
# Use captured Regex match group 1 to rename file with incrementing counter
# and move it to the temporary folder to prevent rename collision with
# existing file
echo mv -- "$f" "$tmpdir/${BASH_REMATCH[1]}_$((n)).png"
fi
# Increment file counter
n=$((n+1))
done
# Move back the renamed files in place
mv --no-clobber -- "$tmpdir/*" ./
# $tempdir removal is automatic on EXIT
# If something goes wrong,some files remain in it and it is not deleted
# so these can be dealt with manually
如果结果符合您的期望,请删除echo
。
样本输出
mv -- 13B12363_1B1_495.png /tmp/tmp.O2HmbyD7d5/13B12363_1B1_11.png
mv -- 13B12363_1B1_496.png /tmp/tmp.O2HmbyD7d5/13B12363_1B1_12.png
mv -- 13B12363_1B1_497.png /tmp/tmp.O2HmbyD7d5/13B12363_1B1_13.png
mv -- 13B12363_1B1_498.png /tmp/tmp.O2HmbyD7d5/13B12363_1B1_14.png
mv -- 13B12363_1B1_499.png /tmp/tmp.O2HmbyD7d5/13B12363_1B1_15.png
,
read
解释\并在IFS上拆分。 bashfaq how to read a stream line by line
在${f%%replacement}
扩展中,替换的不是正则表达式,而是球形。规则不同。 +
的字面意思是+
。
您可以先shopt -o extglob
,然后再${f%%+([0-9]).png}
。或编写一个循环。或者也匹配_
并执行f=${f%%.png}; f="${f%_[0-9]*}_"
。
或者(未经测试):
find . -maxdepth 1 -mindepth 1 -type f -name '13B12363_1B1_*.png' |
sort -t_ -n -k3 |
sed 's/\(.*\)[0-9]+\.png$/&\t\1/' |
{
n=1;
while IFS=$'\t' read -r from to; do
echo mv "$from" "$to$((n++)).png";
done;
}
,
使用perl的另一种选择:
perl -e 'while(<@ARGV>){$o=$_;s/\d+(?=\D*$)/$i++.".renamed"/e;die if -e $_;rename $o,$_}while(<*.renamed>){$o=$_;s/\.renamed$//;die if -e $_;rename $o,$_}' $(ls -v|sed -E "s/$|^/'/g"|paste -sd ' ' -)
此解决方案应通过以下方式避免重命名冲突:首先重命名文件,添加额外的“ .renamed”扩展名。然后删除“ .renamed”扩展名作为最后一步。另外,还有检查来检测重命名冲突。
无论如何,请先备份您的数据,然后再尝试:)
展开并解释了perl脚本:
while(<@ARGV>){ # loop through arguments.
# filenames are passed to "$_" variable
# save old file name
$o=$_;
# if not using variable,regex replacement (s///) uses topic variable ($_)
# e flag ==> evals the replacement
s/\d+(?=\D*$)/$i++.".renamed"/e; # works on $_
# Detect rename collision
die if -e $_;
rename $o,$_
}
while(<*.renamed>){
$o=$_;
s/\.renamed$//; # remove .renamed extension
die if -e $_;
rename $o,$_
}
正则表达式:
\d+ # one number or more
(?=\D*$) # followed by 0 or more non-numbers and end of string