通过反转数字顺序重命名文件

通过反转数字顺序重命名文件

我有很多文件的名称如下:

data1_1.txt
data1_2.txt
data1_3.txt
data2_1.txt
data2_2.txt
...

然而,这些都是以相反的顺序下载和命名的。我怎样才能批量重命名所有这些,以便结果是:

data1_3.txt
data1_2.txt
data1_1.txt
data2_2.txt
data2_1.txt
...

我的第一个想法只是 bash / zsh 脚本,但如果有其他工具可以更好地工作,请告诉我。

答案1

zsh

autoload zmv # best in ~/.zshrc

typeset -A c=()
zmv -n '(*)_<->.txt(#qnOn)' '$1_$((++c[${(b)1}])).txt-renamed' &&
  : zmv '(*)-renamed' '$1'

(如果愿意的话,删除-n(dry-run) 和:(并且记住在不进行空运行的情况下再次运行之前重新初始化c=()))。

  • <->: 类似于<1-12>匹配某个范围内的十进制数字,但这里没有指定界限,因此匹配一个或多个十进制数字的任何序列。也可以写成[0-9]##where ##iszsh相当于 ERE +
  • (#q...)是个明确的指定语法全局限定符
  • n: 按数字排序
  • On: 按名称倒序排序。因此,通过n上面的方法,可以按数字顺序对匹配文件列表进行反向排序。
  • 对于替换,$1包含 中捕获的内容(*),即 之前的部分_<digits>.txt
  • 我们追加$((++c[${(b)1}])),其中$c是之前声明的关联数组。
  • ${(b)1}带有转义的全局字符(没有它,如果包含$1它就无法正常工作)。$1]
  • 我们分两个阶段进行(附加-renamed在第二阶段被删除的后缀),以避免在此过程中覆盖文件。

在你的样本上,这给出了:

mv -- data2_2.txt data2_1.txt-renamed
mv -- data2_1.txt data2_2.txt-renamed
mv -- data1_3.txt data1_1.txt-renamed
mv -- data1_2.txt data1_2.txt-renamed
mv -- data1_1.txt data1_3.txt-renamed

mv -- data1_1.txt-renamed data1_1.txt
mv -- data1_2.txt-renamed data1_2.txt
mv -- data1_3.txt-renamed data1_3.txt
mv -- data2_1.txt-renamed data2_1.txt
mv -- data2_2.txt-renamed data2_2.txt

请注意,从技术上讲,它并不颠倒顺序,或者仅在数字递增 1 并从 1 开始的情况下才执行此操作,如示例中所示。它将把所有[1, 2, 3], [4, 5, 6],[0, 10, 20]变成[3, 2, 1]

要颠倒该列表,会涉及更多一些。它可能是这样的:

all_files=(*_<->.txt(n))
prefixes=(${all_files%_*})

for prefix (${(u)prefixes}) {
  files=(${(M)all_files:#${prefix}_<->.txt})
  new_files=(${(Oa)^files}-renamed)
  for old new (${files:^new_files})
    echo mv -i -- $old $new-renamed
}

(高兴时删除echo)。

并再次运行zmv '(*)-renamed' '$1'作为第二阶段。

在另一个带有附加列表[0, 3, 10, 20]作为第三个示例的示例中,给出:

mv -i -- data1_1.txt data1_3.txt-renamed
mv -i -- data1_2.txt data1_2.txt-renamed
mv -i -- data1_3.txt data1_1.txt-renamed
mv -i -- data2_1.txt data2_2.txt-renamed
mv -i -- data2_2.txt data2_1.txt-renamed
mv -i -- data3_0.txt data3_20.txt-renamed
mv -i -- data3_3.txt data3_10.txt-renamed
mv -i -- data3_10.txt data3_3.txt-renamed
mv -i -- data3_20.txt data3_0.txt-renamed

这些解决方案不假设文件名可能包含什么字符(或非字符),不会重命名文件,除非它们以_<digits>.txt.基于 - 的方法zmv将防止覆盖以-renamed预先存在的后缀命名的文件,而不是后一种方法(尽管-imv在发生这种情况之前提示您)。或者,-renamed您可以将重命名的文件移动到目录中,而不是添加后缀renamed

答案2

这是一个 bash 代码片段,假设文件确实按照您描述的方式命名(data<one-digit>_<digits>.txt)。

shopt -s extglob

#gather files into array
files=( data[[:digit:]]_+([[:digit:]]).txt )

#zip original files with their target file names and feed to mv
paste <(printf '%s\n' "${files[@]}" | sort -k1.5,1n -k2n -t'_') \
    <(printf '%s.ren\n' "${files[@]}" | sort -k1.5,1n -k2nr -t'_') | 
    xargs -n 2 mv --

#strip the temporary .ren suffix
for f in data*.ren; do mv -- "$f" "${f%.ren}"; done

答案3

首先,将所有文件重命名为前缀“old-”:

for i in *
do
    mv "$i" "old-$i"
done

然后运行此命令并观察输出以确保它看起来不错:

ls -v | tac | sort -s -t _ -k1,1 | sed -e 's/^old-//' | paste <(ls -v) - | sed -e 's/^/mv /'

如果是,则将输出通过管道传输到 sh。

这就是发生的事情。

  • ls -v按排序顺序生成它们(例如,-v 表示将 11 排序在 9 之后)
  • tac 反转整个输入(整个文件;请耐心等待!)
  • 排序表示仅对第一个之前的字符进行稳定排序_。这-k1,1 -s对于确保获得正确的输出都很重要。如果没有 -k1,1 ,该行的其余部分将用于解决重复项,这是我们不想要的,并且如果没有-s重复项,则会任意排序。

剩下的就很容易了。

答案4

这是一个简单的解决方案,我相信它非常适合您的情况。我们在这里所做的是,首先检查我们通过前缀获得了多少个文件类型。这里的文件类型前缀是指 data1_, 数据2_等等。然后,对于每种前缀类型,我们获取可用文件的总数并将它们存储到名为 的数组中totalFilesForEachPrefix

然后,在步骤 1 中,我们将该文件重命名为临时文件。移动到扩展的原因otemp是为了避免名称冲突并覆盖现有文件。在这里,我假设您有 1_1 1_2 1_3 1_4 等文件。

然后,在第 2 步中,我们将摆脱.otemp扩展。

#!/usr/bin/env bash
totalFileTypeByPrefix=$( for file in data*_1.txt; do echo $file; done | wc -l )
totalFilesForEachPrefix=()
for (( prefix = 1; prefix <= totalFileTypeByPrefix; prefix++ )); do
  totalFilesForEachPrefix+=( $( for file in data${prefix}_*.txt; do echo $file; done | wc -l) )
done
### Step 1
prefix=1; type=0;
while (( prefix <= totalFileTypeByPrefix )); do
  suffix=${totalFilesForEachPrefix[$type]}
  for file in data${prefix}_*.txt; do
    mv $file data${prefix}_${suffix}.txt.otemp; echo "$file renamed temporary --> data${prefix}_${suffix}.txt.otemp"
    suffix=$((suffix -1))
  done
  type=$((type+1))
  prefix=$((prefix+1))
done
### Step 2
echo "....Finally changing the temporary files"....
for tempfile in *.otemp; do
  file=${tempfile::-6};
  mv $tempfile $file; echo "$tempfile renamed final --> $file";
done

以下是输出以及对所发生情况的解释:

data1_1.txt renamed temporary --> data1_3.txt.otemp
data1_2.txt renamed temporary --> data1_2.txt.otemp
data1_3.txt renamed temporary --> data1_1.txt.otemp
data2_1.txt renamed temporary --> data2_2.txt.otemp
data2_2.txt renamed temporary --> data2_1.txt.otemp
....Finally changing the temporary files....
data1_1.txt.otemp renamed final --> data1_1.txt
data1_2.txt.otemp renamed final --> data1_2.txt
data1_3.txt.otemp renamed final --> data1_3.txt
data2_1.txt.otemp renamed final --> data2_1.txt
data2_2.txt.otemp renamed final --> data2_2.txt

相关内容