如何在Bash脚本中用Awk移除文本文件最后一个.后的内容
问题描述
我有一个名为filename.txt的文件,内容如下:
MOR22A2.S4000011.h23v22.061.2023063111017.hdf MOR22A2.S4000011.h23v44.061.2023061111033.hdf AST006_S003.zip AST003_S066.zip
我需要移除文件名的扩展名——也就是删除最后一个.之后的所有内容。我尝试了这条awk命令:
awk '{print substr($0, 1, index($0, ".") - 1)}' filename.txt
但它只会删除第一个.之后的内容,得到的结果不符合预期:
MOR22A2 MOR22A2 AST006_S003 AST003_S066
我的期望结果是:
MOR22A2.S4000011.h23v22.061.2023063111017 MOR22A2.S4000011.h23v44.061.2023061111033 AST006_S003 AST003_S066
请问在Bash脚本中,应该使用什么正确的awk命令来处理这个文件?
解决方法
之前的命令失效是因为index($0, ".")只会返回第一个.的位置,要实现需求,需要定位到最后一个.,以下是几种可行的awk方案:
方案1:正则替换(最简洁)
利用awk的sub函数,用正则匹配最后一个.及其后续内容并替换为空:
awk '{sub(/\.[^.]*$/, ""); print}' filename.txt
正则\.[^.]*$的含义:匹配行尾前的最后一个.,以及后面所有非.的字符,替换为空即可保留扩展名之前的部分。
如果需要直接修改原文件,可以借助临时文件:
awk '{sub(/\.[^.]*$/, ""); print}' filename.txt > temp.txt && mv temp.txt filename.txt
或者安装moreutils包后使用sponge命令(避免临时文件):
awk '{sub(/\.[^.]*$/, ""); print}' filename.txt | sponge filename.txt
方案2:遍历字符找最后一个.的位置
通过循环遍历每行的每个字符,记录最后一个.的索引,再截取前面的内容:
awk '{ last_dot = 0 for (i=1; i<=length($0); i++) { if (substr($0,i,1) == ".") { last_dot = i } } print (last_dot > 0) ? substr($0,1,last_dot-1) : $0 }' filename.txt
方案3:分割数组后重组
用split函数把每行按.分割成数组,再将除最后一个元素外的所有部分用.拼接起来:
awk '{ n = split($0, arr, ".") if (n > 1) { res = arr[1] for (i=2; i<n; i++) { res = res "." arr[i] } print res } else { print $0 } }' filename.txt
方案4:结合rev命令反向处理
先把每行反转,找到第一个.(对应原行的最后一个.),截取后再反转回来:
rev filename.txt | awk -F. '{print substr($0, index($0,".")+1)}' | rev
内容的提问来源于stack exchange,提问作者sjadhav
相关产品推荐
相关产品推荐

