![替换 Unix 文件夹中多个文件名中的多个字符](https://linux22.com/image/120386/%E6%9B%BF%E6%8D%A2%20Unix%20%E6%96%87%E4%BB%B6%E5%A4%B9%E4%B8%AD%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6%E5%90%8D%E4%B8%AD%E7%9A%84%E5%A4%9A%E4%B8%AA%E5%AD%97%E7%AC%A6.png)
我的 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
使用正则表达式匹配的内容进行填充。然后,将文件移动到该匹配项的小写版本,并附加原始扩展名。我创建了您的所有示例文件并得到了预期的结果。