替换unix中的类似字符

时间:2014-08-04 21:07:25

标签: shell unix awk

我使用以下命令

awk -f script.sh dictionary.txt "test.txt" >> "test2.txt" 

替换.txt中的某些字符。我的脚本如下:

NR == FNR {
  rep[$1] = $2
  next
}

{
    for (key in rep) {
      gsub(key, rep[key])
    }
    print
}

这是我的字典:

I   0:maj[ |]*
i   0:min[ |]*
bII 1:maj[ |]*
II  2:maj[ |]*
ii  2:min[ |]*
bIII    3:maj[ |]*
III 4:maj[ |]*
iii 4:maj[ |]*
IV  5:maj[ |]*
iv  5:min[ |]*
V   7:maj[ |]*
v   7:min[ |]*
bVI 8:maj[ |]*
VI  9:maj[ |]*
vi  9:min[ |]*
bVII    t:maj[ |]*
VII e:maj[ |]*
vii e:min[ |]*

我面临两个问题:

1)II(或ii,III,VI等)不是作为一个独立的实体读取,而是作为两次读取I.我如何指定我的脚本中第一列中的所有内容都应该被读作& #34;块"的人物。我尝试使用(),[],"&#34 ;;什么接缝都没用。我可以使用i {2}来区分ii和i,但我不能像vi这样做。

2)第二列中的空格字符会产生一些问题。我试图逃避它,但它不会工作。

任何技巧?

[edit]这里是.txt的样本

I   ii  V   V   
I   II  V   V   
I   ii  IV  V   
I   ii  III IV  
I   ii  vi  IV  
I   ii  iii IV  
I   II  IV  V   
I   IV  I   ii  
i   ii  V   V   
i   ii  V   V   
i   II  V   V   
i   ii  iv  V   
i   ii  IV  V   
i   ii  vi  IV  

2 个答案:

答案 0 :(得分:3)

以下是其示例输出的替代方案:

$ awk 'NR==FNR{k=$1; $1=""; r[k]=substr($0,2);next} {for (i=1;i<=NF;i++){if ($i in r) {$i=r[$i]}}} {print}' dictionary.txt "test.txt"
0:maj[ |]* 2:min[ |]* 7:maj[ |]* 7:maj[ |]*
0:maj[ |]* 2:maj[ |]* 7:maj[ |]* 7:maj[ |]*
0:maj[ |]* 2:min[ |]* 5:maj[ |]* 7:maj[ |]*
0:maj[ |]* 2:min[ |]* 4:maj[ |]* 5:maj[ |]*
0:maj[ |]* 2:min[ |]* 9:min[ |]* 5:maj[ |]*
0:maj[ |]* 2:min[ |]* 4:maj[ |]* 5:maj[ |]*
0:maj[ |]* 2:maj[ |]* 5:maj[ |]* 7:maj[ |]*
0:maj[ |]* 5:maj[ |]* 0:maj[ |]* 2:min[ |]*
0:min[ |]* 2:min[ |]* 7:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 7:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:maj[ |]* 7:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 5:min[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 5:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 9:min[ |]* 5:maj[ |]*

此方法处理字典值,其中包含空格,或者即使它们中有多个空格。

如何运作

每个部分,一次一个:

  • NR==FNR{k=$1; $1=""; r[k]=substr($0,2);next}

    这将从r文件创建字典dictionary.txt。关键是第一个领域。该值是删除第一个字段后该行剩余的值。删除第一个字段分两步完成:(a)将$1设置为空字符串,(b)使用$1删除现在为空的substr后面的空格功能

  • {for (i=1;i<=NF;i++){if ($i in r) {$i=r[$i]}}}

    这会依次通过每个字段进行替换。由于输入现在被视为字段而不是字符,因此ii等字段不会被误认为连续两个i

  • {print}

    打印带有替换的整行。

gsub

的问题

您遇到的问题是结果取决于从字典中检索密钥的顺序:

for (key in rep) {
  gsub(key, rep[key])
}

例如,如果在i之前检索到ii,则输入ii被视为连续两次i

答案 1 :(得分:1)

不要使用gsub,只需浏览每个字段并进行直接交换:

NR == FNR {    
    rep[$1] = $2 " " $3
    next
}

{
    for (i=1; i<=NF; ++i) {
        if ($i in rep) $i = rep[$i]
    }
    print
}

或者如果你喜欢单行:

awk 'NR==FNR{rep[$1]=$2" "$3;next}{for(i=1;i<=NF;++i)if($i in rep)$i=rep[$i]}1' dictionary.txt test.txt

您的方法存在的问题是ii确实与正则表达式/i/以及/ii/匹配,因此gsub会在数组中首先遇到它所遇到的替换。使用这种方法,您可以交换完全匹配的密钥的值。请注意,我也更改了rep的分配,以处理[ |]*之间的空格,因为awk将这些视为两个不同的字段。

输出:

0:maj[ |]* 2:min[ |]* 7:maj[ |]* 7:maj[ |]*
0:maj[ |]* 2:maj[ |]* 7:maj[ |]* 7:maj[ |]*
0:maj[ |]* 2:min[ |]* 5:maj[ |]* 7:maj[ |]*
0:maj[ |]* 2:min[ |]* 4:maj[ |]* 5:maj[ |]*
0:maj[ |]* 2:min[ |]* 9:min[ |]* 5:maj[ |]*
0:maj[ |]* 2:min[ |]* 4:maj[ |]* 5:maj[ |]*
0:maj[ |]* 2:maj[ |]* 5:maj[ |]* 7:maj[ |]*
0:maj[ |]* 5:maj[ |]* 0:maj[ |]* 2:min[ |]*
0:min[ |]* 2:min[ |]* 7:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 7:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:maj[ |]* 7:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 5:min[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 5:maj[ |]* 7:maj[ |]*
0:min[ |]* 2:min[ |]* 9:min[ |]* 5:maj[ |]*