AWK, SED, REGEX 重命名文件



我只是在学习使用REGEX,AWK和SED。我目前有一组我想重命名的文件 - 它们都位于一个目录中。

命名模式是一致的,但我想重新排列文件名,格式如下:

01._HORRIBLE_HISTORIES_S2.mp4
02._HORRIBLE_HISTORIES_S2.mp4

我想将它们重命名为 HORRIBLE_HISTORIES_s01e01.mp4 - 其中 e01 是从第一列收集的。 我知道我想从第一列中抓取"01",将其填充到变量中,然后将其粘贴在每个文件名的 S2 之后,同时我想将其与"._"一起从文件名的开头删除,此外我想将"S2"更改为"s02"。

如果有人这么好心,你能帮我写一些使用 awk/sed 的东西并解释这个过程,我可以从中学习吗?

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}"中的数字左边填充为带有0的2位数字(即,仅当数字还没有2位数字时才在前面加上0)并存储在变量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只是在前面加上

如上所述,您需要先删除echo,然后再mv执行实际重命名。

根据模式重命名多个文件的常用方法是使用 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)。这里有一个类似的讨论 SO 关于查找/安装正确命令的更多详细信息。

你可以用几乎纯bash(使用变量扩展)来做到这一点:

for f in *mp4 ; do
newfilename="${f:5:20}_s01e${f:1:2}.mp4"
echo mv $f $newfilename
done

如果此命令的输出符合您的需求,您可以从循环中删除echo,或者更简单地说(如果您的最后一个命令是上述命令)问题:!! | bash

使用 AWK 重命名文件。

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

将文件名字符串转换为文本文件,然后使用 loop 和 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

如果你愿意使用gawk,正则表达式匹配真的派上用场了。我发现这种基于管道的解决方案比担心循环结构要好一些。

ls -1 | 
gawk 'match($0, /.../, a) { printf ... | "sh" } 
END { close("sh") }'

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

  • 第 1 行列出了当前目录中的所有文件名,每行一行,并将其通过管道传输到 gawk 命令。
  • 第 2 行运行正则表达式匹配,将捕获的组分配给数组变量a。该操作将其转换为我们想要的命令,printf该命令本身通过管道传输到要执行sh
  • 第 3 行关闭了我们开始向其输送东西时隐式打开的外壳。

因此,您只需填写正则表达式和命令语法(借用mklement0)。例如(实时代码警告):

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

要预览该命令(您应该这样做),您只需从第二行中删除| "sh"即可。

最新更新