如何使用awk/sed/grep正确替换文件中以#开头的单词
需求说明
替换文件内所有以#开头的单词/字符串,替换规则为:将单词开头的#替换为fruit=前缀,单词中间包含#的内容不做任何改动。
示例参考
原文件file.txt内容
$ cat file.txt #apple sd#kf #banana adfe #apple we#re #banana cow
预期输出结果
$ cat output.txt fruit=apple sd#kf fruit=banana adfe fruit=apple we#re fruit=banana cow
之前尝试的问题复盘
之前两种命令未得到正确结果,原因如下:
- awk命令错误:执行
awk '{for (i=1; i<=NF; i++) $i~/^#/ && $i="fruit="$i }1'时,直接将带开头#的原字段拼接到fruit=后,没有移除字段开头的#,最终输出为fruit=#apple格式,不符合要求,实际错误输出:
fruit=#apple sd#kf fruit=#banana adfe fruit=#apple we#re fruit=#banana cow
- sed命令错误:执行
sed 's/^#/fruit=/g'时,正则^#仅能匹配行首位置的#,无法匹配行内空白字符后、单词开头的#,导致行内的#banana这类内容未被替换,实际错误输出:
fruit=apple sd#kf #banana adfe fruit=apple we#re #banana cow
正确实现方案
awk实现
逐字段遍历,判断字段是否以#开头,若是则先移除字段开头的#,再拼接fruit=前缀,命令如下:
awk '{ for(i=1;i<=NF;i++){ if($i ~ /^#/){ sub(/^#/,"",$i) $i = "fruit=" $i } } print }' file.txt > output.txt
原理:awk默认按空白字符分割字段,逐字段判断开头标记时不会误触单词中间的#,替换时先删掉开头的#再拼接前缀,输出格式符合要求。
sed实现
正则匹配两种位置的开头#:行首位置的#、空白字符后紧跟的单词开头#,捕获#前的位置内容,替换时保留前置内容再拼接fruit=,命令如下:
sed -E 's/(^|[[:space:]])#/\1fruit=/g' file.txt > output.txt
原理:正则(^|[[:space:]])#不会匹配单词中间的#(这类#前为普通字母,不是行首也不是空白字符),全局替换时不会改动sd#kf、we#re这类含中间#的内容。
grep配合实现
grep本身仅支持文本匹配检索,无原生替换能力,可先用grep做规则校验,确认待替换内容匹配准确后再执行替换:
- 先校验所有待替换的目标串,确认没有误匹配单词中间的#:
grep -oE '(^|[[:space:]])#[^[:space:]]+' file.txt
执行后会输出所有命中的待替换内容,确认输出只有#apple、#banana这类开头带#的单词,没有sd#kf、we#re这类内容,说明匹配规则准确。
2. 校验通过后,可直接调用前文的sed/awk命令完成替换,也可将grep匹配到的行传给sed处理:
grep -E '(^|[[:space:]])#' file.txt | sed -E 's/(^|[[:space:]])#/\1fruit=/g' > output.txt
内容的提问来源于stack exchange,提问作者Sharad Shrestha
相关产品推荐
相关产品推荐

