使用awk/sed按条件移除制表分隔文件指定列首词并裁剪内容
制表符文件第三列规则处理方案
需求说明
需要处理制表符分隔的文本文件,仅对第三列执行指定规则转换,其余列原样保留输出,第三列处理规则:
- 规则1:同时满足两个条件时,移除第三列第一个单词:① 第一个单词全为小写字母;② 第二个单词首字母大写
- 规则2:完成规则1处理后判断:如果该列整段字符串并非全小写,仅保留前2个单词,裁剪多余内容;如果整段全小写则保留全部内容
优先awk实现,sed方案也可接受。
现存问题
原有sed+awk组合管道只能单独提取第三列处理,无法保留其他列输出完整行;不清楚如何让sed仅匹配指定列模式,也不知道awk无反向引用时如何实现匹配逻辑。
原有问题代码:
cat text.txt | awk -F" " '{print $3}' | sed -E 's/^[a-z]* ([A-Z])/\1/' | awk '{if($1 ~ /^[[:upper:]]/) {print $1, $2} else {print $0}}'
测试样例
输入文件内容
col1 col2 col3 123 a string James jones MD MSc 154 string mister George smith 163 String mrs anne jones 193 String john 157 big string 1 dude George
期望输出
col1 col2 col3 123 a string James jones 154 string George smith 163 String mrs anne jones 193 String john 157 big string 1 George
纯awk实现方案
不需要依赖sed,也不需要正则反向引用,直接把第三列拆分为单词数组逐段处理即可,逻辑清晰且不会丢失其他列内容:
awk -F'\t' 'BEGIN{OFS="\t"} { # 将第三列按空格拆分为单词数组 word_cnt = split($3, words, /[[:space:]]+/) # 执行规则1:判断是否需要移除第一个单词 if (word_cnt >= 2 && words[1] ~ /^[a-z]+$/ && substr(words[2], 1, 1) ~ /[A-Z]/) { for (i = 1; i < word_cnt; i++) { words[i] = words[i+1] } word_cnt-- } # 判断处理后的第三列是否全为小写 all_lower = 1 for (i = 1; i <= word_cnt; i++) { if (words[i] !~ /^[a-z]+$/) { all_lower = 0 break } } # 执行规则2:拼接最终第三列内容 new_col3 = "" if (all_lower) { # 全小写保留全部内容 for (i = 1; i <= word_cnt; i++) { new_col3 = new_col3 (i>1 ? " " : "") words[i] } } else { # 非全小写最多保留前2个单词 keep_cnt = word_cnt >= 2 ? 2 : word_cnt for (i = 1; i <= keep_cnt; i++) { new_col3 = new_col3 (i>1 ? " " : "") words[i] } } $3 = new_col3 print }' text.txt
逻辑说明
- 指定输入、输出分隔符均为制表符,保证非处理列格式、内容完全不变
- 针对第三列单独拆分为单词数组操作,不需要正则反向引用,避免正则匹配边界问题
- 处理完成后直接替换第三列值打印整行,不会出现列丢失问题
- 兼容表头行、单词数不足2个的边界场景
内容的提问来源于stack exchange,提问作者Andy
相关产品推荐
相关产品推荐

