我不确定这是否可以在sed(或awk或任何bash工具)中执行我想要的操作:
我想用< happy>创建一个用字符串替换:)的脚本.和):由< sad>.这可以通过sed轻松完成:
- echo "test : )" | sed 's/: )/<happy>/g'
- echo "test ) :" | sed 's/) :/<sad>/g'
不幸的是,有时我会有这样的字符串:
- I'm happy : ) : ) : )
- I'm sad ) : ) : ) :
在这种情况下,输出应该是:
- I'm happy <happy> <happy> <happy>
- I'm sad <sad> <sad> <sad>
但通过结合上面的两个命令:
- echo "I'm happy : ) : ) : )" | sed 's/: )/<happy>/g' | sed 's/) :/<sad>/g'
- echo "I'm sad ) : ) : ) :" | sed 's/: )/<happy>/g' | sed 's/) :/<sad>/g'
我会得到:
- I'm happy <happy> <happy> <happy>
- I'm sad ) <happy> <happy> :
解决这个问题的方法是通过从左到右处理字符串来并行地进行两个替换.我尝试使用这样的东西:sed’s / a / b / g; s / c / d / g’但是替换只能在一个模式之后完成,并且不能解决问题.
解决方法
使用GNU awk为第3个arg匹配():
- $cat script1.awk
- BEGIN {
- map[": )"] = "<happy>"
- map[") :"] = "<sad>"
- }
- {
- while ( match($0,/(.*)(: \)|\) :)(.*)/,a) ) {
- $0 = a[1] map[a[2]] a[3]
- }
- }
- $awk -f script1.awk file
- I'm happy <happy> <happy> <happy>
- I'm sad <sad> <sad> <sad>
有任何awk:
- $cat script2.awk
- BEGIN {
- map[": )"] = "<happy>"
- map[") :"] = "<sad>"
- }
- {
- while ( match($0,/: \)|\) :/) ) {
- $0 = substr($0,1,RSTART-1) map[substr($0,RSTART,RLENGTH)] substr($0,RSTART+RLENGTH)
- }
- }
- $awk -f script2.awk file
- I'm happy <happy> <happy> <happy>
- I'm sad <sad> <sad> <sad>
虽然在这种情况下两种方法都产生相同的输出,但第一种方法实际上是从字符串的结尾到前面的礼貌.*而第二种方法从前到后工作.您可以通过此测试看到:
- $echo ': ) :' | awk -f script1.awk
- : <sad>
- $echo ': ) :' | awk -f script2.awk
- <happy> :
您可以使用任何具有调整功能的awk进行从前到后的传递,但我不认为这是您真正想要的.
编辑以从地图构建正则表达式:
- $cat tst.awk
- BEGIN {
- map[": )"] = "<happy>"
- map[") :"] = "<sad>"
- for (emoji in map) {
- gsub(/[^^]/,"[&]",emoji)
- gsub(/\^/,"\\^",emoji)
- emojis = (emojis == "" ? "" : emojis "|") emoji
- }
- }
- {
- while ( match($0,emojis) ) {
- $0 = substr($0,RSTART+RLENGTH)
- }
- }
- $awk -f tst.awk file
- I'm happy <happy> <happy> <happy>
- I'm sad <sad> <sad> <sad>