使用脚本重命名文本文件

使用脚本重命名文本文件

我的文件夹中有数千个文本文件,它们以某种方式命名。

例如:

Image_234_Data_7778_n0.txt
Image_234_Data_7778_n1.txt
Image_234_Data_7778_n2.txt

Image_954_Data_4478_n0.txt
Image_954_Data_4478_n1.txt
Image_954_Data_4478_n2.txt

Image_104_Data_9878_n0.txt
Image_104_Data_9878_n1.txt
Image_104_Data_9878_n2.txt

等等...

我想制作一个源文件(它将用于重命名文件并跟踪“哪个是哪个”),其内容应为:

1_1.txt:Image_234_Data_7778_n0.txt
1_2.txt:Image_234_Data_7778_n1.txt
1_3.txt:Image_234_Data_7778_n2.txt

2_1.txt:Image_954_Data_4478_n0.txt
2_2.txt:Image_954_Data_4478_n1.txt
2_3.txt:Image_954_Data_4478_n2.txt

3_1.txt:Image_104_Data_9878_n0.txt
3_2.txt:Image_104_Data_9878_n1.txt
3_3.txt:Image_104_Data_9878_n2.txt

等等...

任何人都可以帮我编写一些可以为我完成此操作的代码吗?

答案1

我假设您的文件名的形式是XXXXNNN.txt一些XXXX不以数字结尾的任意文本,并且NNN是数字序列,并且您希望按相同 XXX 的组对它们进行分组。

策略:按字典顺序循环遍历文件,并检测 XXXX 部分何时发生变化。为每个组生成新名称。一个小复杂之处是,如果 NNN 部分是可变宽度的,那么它们不会按字典顺序排序:NNN=10 将出现在 NNN=1 和 NNN=2 之间。

current=
numbers=
i=0
for x in *.txt ''; do
  stem=${x%.*}
  n=${stem##*[!0-9]}
  stem=${stem%$n}
  if [ "$stem" != "$current" ]; then
    for k in $(printf '%s\n' $numbers | sort -n); do
      y=${i}_${k}.txt
      echo mv "$current$k.txt" "$y"
    done
    current=$stem
    numbers=$n
    i=$((i+1))
  else
    numbers="$numbers $n"
  fi
done

替换echo mv为您要使用的命令,例如mv重命名文件或echo …将信息写入文件。

不要重命名文件并跟踪旧名称,而是考虑使用符号链接以便可以使用文件的原始名称和简化名称来访问这些文件。

答案2

创建包含以下文件的测试目录后:

Image_104_Data_9878_n0.txt, Image_104_Data_9878_n1.txt,
Image_234_Data_7778_n0.txt, Image_234_Data_7778_n1.txt,
Image_234_Data_7778_n2.txt, Image_954_Data_4478_n0.txt,
Image_954_Data_4478_n1.txt, Image_954_Data_4478_n2.txt

然后我做了:

printf %s\\n * | sort --debug -t_ -k2,2n -k5.2n,5.2n 

结果是:

Image_104_Data_9878_n0.txt
      ___
                     _
__________________________
Image_104_Data_9878_n1.txt
      ___
                     _
__________________________
Image_234_Data_7778_n0.txt
      ___
                     _
__________________________
Image_234_Data_7778_n1.txt
      ___
                     _
__________________________
Image_234_Data_7778_n2.txt
      ___
                     _
__________________________
Image_954_Data_4478_n0.txt
      ___
                     _
__________________________
Image_954_Data_4478_n1.txt
      ___
                     _
__________________________
Image_954_Data_4478_n2.txt
      ___
                     _
__________________________

我告诉主要按 2cd分隔字段上从字段开头到字段结尾的sort数字进行排序,例如,其次对第 5 个字段中的第二个 2cd 字节进行数字排序,例如。我要求它提供输出,这样它就会告诉我它到底在做什么。_-k2,2n-k5.2,5.2n--debug

我可以轻松地主要在第 4 个字段上排序,或者主要在字段 2 上排序,其次在字段 5.2 上排序,最不重要的在第二个字段上排序。我之所以这么说,是因为我无法确定您的示例中提供的排序的任何押韵或原因,我只能假设您将它们分配为:

  • 1_1:234/7778
  • 2_1:954/4478
  • 3_1:104/9878

...因为到目前为止,您还没有制定任何适当的命令来对它们进行排序,也许您需要一些关于如何进行排序的建议。根据这个假设,我将这样做:

printf %s\\n * | 
sort -t_ -k4,4n -k5.2n,5.2n | 
nl -bp'_n0\.' -s_ |
sed 's/\(I[^.]*_n\)\(.*\)/\2:\1\2/;N
     s/ *\([0-9]*_\)\(.*\n\) *\([^_]*I\)/\1\2\1\3/;P;D'

我认为这会产生与您正在寻找的结果非常接近的结果。看?

1_0.txt:Image_954_Data_4478_n0.txt
1_1.txt:Image_954_Data_4478_n1.txt
1_2.txt:Image_954_Data_4478_n2.txt
2_0.txt:Image_234_Data_7778_n0.txt
2_1.txt:Image_234_Data_7778_n1.txt
2_2.txt:Image_234_Data_7778_n2.txt
3_0.txt:Image_104_Data_9878_n0.txt
3_1.txt:Image_104_Data_9878_n1.txt

在那里,它们按第四个字段进行排序和编号,因为我指定了该-k4,4n字段,sort但您可以轻松地-k2,2n按照说明进行操作。

该命令的工作原理是要求nl仅对包含字符串的行进行编号_n0.sed接收其输出,如下所示:

 1_Image_954_Data_4478_n0.txt
   Image_954_Data_4478_n1.txt
   Image_954_Data_4478_n2.txt
 2_Image_234_Data_7778_n0.txt
   Image_234_Data_7778_n1.txt
   Image_234_Data_7778_n2.txt
 3_Image_104_Data_9878_n0.txt
   Image_104_Data_9878_n1.txt

...并首先将该_n[0-9]*.txt位复制到行首,Next 拉入下一行,并且,如果模式空间如下所示:

 *num_.*\n [^_]*I

...此时它将第一行中的 num 位附加到第二行上。如果您想从该命令生成的文本文件转到移动操作,您可以执行以下操作:

sed 's/\([^:]*\):\(.*\)/$* \2 \1/' <txtfile |
sh -s -- echo mv

输出

mv Image_954_Data_4478_n0.txt 1_0.txt
mv Image_954_Data_4478_n1.txt 1_1.txt
mv Image_954_Data_4478_n2.txt 1_2.txt
mv Image_234_Data_7778_n0.txt 2_0.txt
mv Image_234_Data_7778_n1.txt 2_1.txt
mv Image_234_Data_7778_n2.txt 2_2.txt
mv Image_104_Data_9878_n0.txt 3_0.txt
mv Image_104_Data_9878_n1.txt 3_1.txt

在那里它只是echoed 因为这是 shell 进程的第一个参数,但是如果你删除它并运行它,就像我刚刚所做的那样,你会得到相同的结果:

ls -m

1_0.txt, 1_1.txt, 1_2.txt, 2_0.txt, 2_1.txt, 2_2.txt, 3_0.txt, 3_1.txt

Gilles 推荐了链接,我也认为这是一个很好的主意,但如果可以的话,我个人会避开软链接,只做一个镜像硬链接目录。您可以用几乎完全相同的方式做到这一点,但您希望使用ln而不是mv.

答案3

解决此类问题的一个技巧(特别是如果您不太擅长编写脚本)是使用电子表格生成脚本。如果您尝试构建可重复使用的脚本,这不是一个好习惯,但对于那些没有时间学习以另一种方式编写脚本的人来说,这对于一次性工作来说可能很方便。

看起来您想要完成的重命名(或链接)是将 a 之前的所有内容更改_n为 a 之前的单个序列号_,并保留后面的部分_n不变。如果这不是您的确切意图,则修改电子表格公式很容易。

例如(缩短文件名以减少横向滚动的需要):

A2               B2             C2           D2           E2
i234d7778_n0.txt =FIND("_n",A2) =LEFT(A2,B2) =D1+(C2<>C1) =D2&"_"&RIGHT(A2,LEN(A2)-B2-1)
i234d7778_n0.txt 10             i234d7778_   1            1_0.txt

从第 2 行(而不是第 1 行)开始的原因是,D2 中的比较会转到第 1 行。(另一种方法是将其放在第一行,但特殊情况下 D1 会放在第 1 行。)

D2 中的表达式只是一种简洁的表达方式:=IF(C2=C1,D1,D1+1)

与电子表格一样,将文件列表粘贴到 A 列中,然后根据文件的数量重复 B 列到 E 列。要生成执行重命名的脚本,您可以添加“F”或“G”列:

F2                          G2
="mv "&A2&" "&E2            ="ln "&A2&" new-name-directory/"&E2
mv i234d7778_n0.tx 1_0.txt  ln i234d7778_n0.tx new-name-directory/1_0.txt

F 和 G 列提供可执行脚本的文本。

相关内容