使用AWK,SED和REGEX重命名文件

15

我只是在学习使用正则表达式(REGEX)、AWK和SED。我目前有一组文件需要重命名,它们都位于同一个目录中。

这些文件的命名模式是一致的,但我想重新排列它们的文件名,以下是格式:

01._HORRIBLE_HISTORIES_S2.mp4
02._HORRIBLE_HISTORIES_S2.mp4
我想将它们重命名为HORRIBLE_HISTORIES_s01e01.mp4——其中e01是从第一列中提取的。我知道我想要从第一列中获取“01”,将其放入一个变量中,然后在每个文件名的S2之后粘贴它,在同一时间,我想将它从文件名的开头以及“._”一起删除,此外,我想将“S2”更改为“s02”。
如果有人好心,能否帮助我编写使用awk/sed的代码并解释一下过程,这样我就可以学习呢?
6个回答

15
for f in *.mp4; do 
  echo mv "$f" \
    "$(awk -F '[._]' '{ si = sprintf("%02s", substr($5,2)); 
                          print $3 "_" $4 "_s" si "e" $1 "." $6 }' <<<"$f")"
done 
  • 循环遍历所有 *.mp4 文件。
  • 使用命令替换($(...))提供的 awk 命令对每个文件进行重命名。
  • awk 命令将输入文件名按 . 或 "_" 分割成多个标记,第一个标记为 $1,第二个为 $2,以此类推。
  • 首先,从 "_S{number}" 中提取数字并在左侧填充零,使其成为两位数,并存储在变量 si(季度索引)中;如果总是在数字前面添加 0,则 awk "程序" 可以简化为:{ print $3 "_" $4 "_s0" substr($5,2) "e" $1 "." $6 }
  • 然后将结果与剩余标记重新排列以形成所需的文件名。

请注意,在 mv 前加上 echo 可以安全地预览生成的命令 - 移除它以执行实际重命名。

另一种方法:使用正则表达式的纯 bash 解决方案:

for f in *.mp4; do 
  [[ $f =~ ^([0-9]+)\._([^.]+)_S([^.]+)\.(.+)$ ]]
  echo mv "$f" \
"${BASH_REMATCH[2]}_s0${BASH_REMATCH[3]}e${BASH_REMATCH[1]}.${BASH_REMATCH[4]}"
done 
  • 使用bash的正则表达式匹配操作符=~,带有捕获组(在(...)中的子字符串)以匹配每个文件名并提取感兴趣的子字符串。
  • 匹配结果存储在特殊的数组变量$BASH_REMATCH中,元素0包含整个匹配项,1包含第一个捕获组的匹配项,2是第二个,以此类推。
  • mv命令的目标参数然后按所需顺序组装捕获组匹配项;请注意,在这种情况下,为了简单起见,将s{number}的零填充设置为无条件-只需在前面添加0

与上面一样,您需要在mv之前删除echo以执行实际重命名。


10

重命名多个文件的常见方法是使用Perl命令rename。它使用Perl正则表达式,非常强大。使用-n -v测试模式而不触碰文件:

$ rename -n -v 's/^(\d+)._(.+)_S2\.mp4/$2_s02e$1.mp4/' *.mp4
01._HORRIBLE_HISTORIES_S2.mp4 renamed as HORRIBLE_HISTORIES_s02e01.mp4
02._HORRIBLE_HISTORIES_S2.mp4 renamed as HORRIBLE_HISTORIES_s02e02.mp4

使用括号将字符串捕获到变量$1(第一个捕获),$2(第二个捕获)等中:

  • ^(\ d +) 捕获文件名开头的数字(放入$1中)
  • ._(.+)_S2\.mp4 捕获.__S2.mp4之间的所有内容(放入$2中)
  • $2_s02e $1.mp4 按您想要的方式组装新的文件名

当您对结果满意时,请从命令中删除-n,它将真正重命名所有文件。

rename在Linux上通常默认可用(软件包util-linux)。 这里有一个类似的讨论,其中提供了有关查找/安装正确命令的更多详细信息。


7
您可以几乎完全使用纯粹的bash(使用变量扩展)来实现这一点:
for f in *mp4 ; do
  newfilename="${f:5:20}_s01e${f:1:2}.mp4"
  echo mv $f $newfilename
done

如果这个命令的输出符合您的需要,您可以从循环中移除echo,或者更简单的方法(如果您上一个命令是以上内容)是执行: !! | bash

我认为这仅适用于固定大小的字符串。如果要在任意大小的字符串上替换扩展名,该怎么办? - sinekonata

2
使用 AWK。将文件重命名为第一、二、四部分。最初的回答为:

使用 AWK。重命名文件时只保留第一、第二和第四部分。

ls | while read file; do newfile=`echo $file | awk -F . '{print $1 "." $2 "." $4}'`; echo $newfile;  mv $file $newfile; done;

1
如果您愿意使用gawk,正则表达式匹配非常方便。我发现这个基于管道的解决方案比担心循环结构更好。
ls -1 | \
    gawk 'match($0, /.../, a) { printf ... | "sh" } \
    END { close("sh") }'

为了方便阅读,我用省略号代替了正则表达式和mv命令。

  • 第一行列出当前目录中的所有文件名,每行一个,并将其传输到gawk命令。
  • 第二行运行正则表达式匹配,将捕获组分配给数组变量a。操作使用printf将其转换为我们所需的命令,该命令本身被传输到sh以执行。
  • 第三行关闭了我们开始将事物传输到其中的shell。

因此,您只需填写正则表达式和命令语法(借鉴mklement0)。例如(LIVE CODE WARNING):

ls -1 | \
    gawk 'match($0, /^([0-9]+)\._([^.]+)_S([^.]+)\.(.+)$/, a) { printf "mv %s %s_s0%se%s.%s\n",a[0],a[2],a[3],a[1],a[4] | "sh" } \
    END { close("sh") }'

为了预览该命令(正如您应该做的那样),您只需从第二行中简单地删除| "sh"即可。

1
将文件名字符串变成文本文件,然后使用循环和awk重命名文件。
while read oldname; do
  newname=$(awk -F'.' '{ print substr($2, 2) "_e" $1 "." $3 }' <<< ${oldname} | \
        awk -F'_' '{ print $1 "_s0" substr($2, 2) $3 }');
  mv ${oldname} ${newname};
done<input.txt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接