替换 Unix 文件夹中多个文件名中的多个字符

替换 Unix 文件夹中多个文件名中的多个字符

我的 Unix 文件夹中有几个文件,比方说 /home/TRANS。

此文件夹每月接收文件。文件名如下:

  • ENCD_213_E-DM_CCA_ID3490_A01.txt
  • ENCD_213_E-DM_CCA_ID33120_A01.txt
  • ENCD_213_E-DM_CCA_IDP3664_A01.txt
  • ENCD_213_E-DM_CCA_ID3327_A01.txt
  • ENACT_215_E_DM_CCA_IDA33320_25OCT2017.csv
  • ENACT_215_E_DM_CCA_IDA31116_25OCT2017.csv

重命名后,最终输出应该是:

  • id3490.txt
  • id33120.txt
  • idp3664.txt
  • id3327.txt
  • ida33320.csv
  • ida31116.csv

因此,本质上,我希望在 TRANS 文件夹中的所有文件名中替换以下字符串,并将最终输出文件名设为小写:

  • ENCD_213_E-DM_CCA_
  • _A01
  • ENACT_215_E_DM_CCA_
  • _2017年10月25日

如何在单行命令或 shell 脚本中执行此操作?我确实检查了多个问题,但除了更改为小写之外,找不到多个字符的替换。需要你的帮助,因为我对 Unix 还很陌生。

答案1

有很多方法可以做到这一点。这是一个适用于您的示例的命令:

for oldname in *; do
    newname="$(echo "${oldname}" | grep -Po 'ID\w?\d+' | tr A-Z a-z).txt" \
    && mv -i "${oldname}" "${newname}";
done

该示例使用了与 Perl 兼容的正则表达式,您似乎grep不支持该表达式。这是使用基本正则表达式的替代方案:

for oldname in *; do
    newname="$(echo "${oldname}" | grep -o 'ID[A-Z]\?[0-9]\+' | tr A-Z a-z).txt" \
    && mv -i "${oldname}" "${newname}";
done

下面是更详细的解释。


该表达式for oldname in *使用通配符模式/通配符迭代当前目录中的文件并将每个名称存储在oldname变量中。要测试这一点,您可以运行以下命令:

for oldname in *; do echo "${oldname}"; done

然后我们用来grep提取您想要保留的文件名部分。选项-P标志指示grep使用 Perl 兼容的正则表达式(在这种情况下并不是真正必要),并且该-o标志指示grep仅提取匹配的子字符串(而不是打印整个字符串)。该\w?模式与选项单个字符(“单词”字符)匹配,并且该\d+模式与一个或多个数字匹配。我们可以像这样测试正则表达式:

for oldname in *; do echo "${oldname}" | grep -Po 'ID\w?\d+'; done

然后我们使用tr将大写字符转换为小写:

for oldname in *; do echo "${oldname}" | grep -Po 'ID\w?\d+' | tr A-Z a-z; done

下一步是使用命令替换将此字符串分配给变量,然后打印结果:

for oldname in *; do
    newname="$(echo "${oldname}" | grep -Po 'ID\w?\d+' | tr A-Z a-z)" && echo "${newname}"
done

然后我们添加“.txt”文件扩展名:

for oldname in *; do
    newname="$(echo "${oldname}" | grep -Po 'ID\w?\d+' | tr A-Z a-z).txt" && echo "${newname}"
done

我们可以运行此命令作为健全性检查,以确保我们得到预期的结果。一旦我们满意,我们就echo用以下命令替换该命令mv

for oldname in *; do
    newname="$(echo "${oldname}" | grep -Po 'ID\w?\d+' | tr A-Z a-z).txt" \
    && mv -i "${oldname}" "${newname}";
done

答案2

find+bash解决方案:

find . -type f -regextype posix-egrep \
    -regex ".*EN(ACT|CD)_[0-9]+_E(-|_)DM_CCA_.+[0-9]\.(txt|csv)$" -exec bash -c \
    'fn=${0##*/}; dir_n="${0%/*}/"; 
    [[ "$fn" =~ .*_(ID[^_]+)_.*\.(txt|csv)$ ]]; 
    mv "$0" "$dir_n${BASH_REMATCH[1],,}.${BASH_REMATCH[2]}"; ' {} \;

答案3

对于文件名操作,bash 参数扩展非常好。看以获得良好的概览。

由于您基本上想要保留文件的 ID 部分,因此可以这样做:

#!/bin/bash

for f in *csv *txt; do
   ext="${f##*.}"

   if [[ $f =~ ID[[:alnum:]]+ ]]; then
      mv "$f" "${BASH_REMATCH,,}.${ext}"
   fi

done

exit

这会循环遍历每个 csv 和 txt 文件并使用 PE 获取扩展名。然后,使用 bash 正则表达式匹配运算符=~检查文件名是否与您的模式匹配。如果是,bash 将$BASH_REMATCH使用正则表达式匹配的内容进行填充。然后,将文件移动到该匹配项的小写版本,并附加原始扩展名。我创建了您的所有示例文件并得到了预期的结果。

相关内容