如何用不同的模式替换模式列表?

时间:2019-01-24 20:35:36

标签: bash awk sed

我尝试使用sedawk在其他人的文件中更改单词。

我有1个fileA,像这样:

((A,(B,(C,D))),(E));  
((A,B),C),D),(E));  

和第二个fileB,其模式要更改:

A (foo,bar,foox,barn,foon) 
B (cat,dog,sheep,abc)  
C (cadd,dget,vdhfu,dssu,dfhty,dueit)  
D (cdfte,shdgt,cdht,ddht,ddh)  
E (cdc,addge) 

我想在fileA中将值ABCDE替换为图案文件。


我的尝试

while read n k; do sed -i.bak "s/$k/$n/g" fileA; done < fileB

6 个答案:

答案 0 :(得分:1)

<button onclick="document.getElementById('myImage').src='pic_bulboff.gif'">Turn off the light</button>

内部sed "$(sed 's/^\([^ ]*\) \(.*\)$/s#\1#\2#g/' fileB)" fileA 将行从sed转换为fileB。 然后,使用参数作为第一个的输出运行下一个sed。

对于输入文件s/<pattern>/<pattern>/g,内部fileB将打印:

sed

可以传递给外部s#A#(foo,bar,foox,barn,foon)#g s#B#(cat,dog,sheep,abc)#g s#C#(cadd,dget,vdhfu,dssu,dfhty,dueit)#g s#D#(cdfte,shdgt,cdht,ddht,ddh)#g s#E#(cdc,addge)#g 来执行。

jdoodle上进行了以下测试:

sed

我得到以下输出:

cat <<EOF >fileA
((A,(B,(C,D))),(E));  
((A,B),C),D),(E)); 
EOF

cat <<EOF >fileB
A (foo,bar,foox,barn,foon)
B (cat,dog,sheep,abc)
C (cadd,dget,vdhfu,dssu,dfhty,dueit)
D (cdfte,shdgt,cdht,ddht,ddh)
E (cdc,addge)
EOF

sed "$(sed 's/^\([^ ]*\) \(.*\)$/s#\1#\2#g/' fileB)" fileA

答案 1 :(得分:1)

您也可以尝试Perl。

$ cat nico_fileA
((A,(B,(C,D))),(E));
((A,B),C),D),(E));

$ cat nico_fileB
A (foo,bar,foox,barn,foon)
B (cat,dog,sheep,abc)
C (cadd,dget,vdhfu,dssu,dfhty,dueit)
D (cdfte,shdgt,cdht,ddht,ddh)
E (cdc,addge)

$ perl -pe ' BEGIN { %kv=map{chomp;split} qx(cat nico_fileB) } s/([A-E])/$kv{$1}/g ' nico_fileA
(((foo,bar,foox,barn,foon),((cat,dog,sheep,abc),((cadd,dget,vdhfu,dssu,dfhty,dueit),(cdfte,shdgt,cdht,ddht,ddh)))),((cdc,addge)));
(((foo,bar,foox,barn,foon),(cat,dog,sheep,abc)),(cadd,dget,vdhfu,dssu,dfhty,dueit)),(cdfte,shdgt,cdht,ddht,ddh)),((cdc,addge)));

$

答案 2 :(得分:1)

在这种情况下要担心的一件事情是,替换的之一是否包含替换的之一。例如,如果您正在查看原始文本

AfooB

并替换为

A B
B C

您想以BfooC结尾,但是如果连续进行全文替换,则:

sed -i 's/A/B/g' file
sed -i 's/B/C/g' file

您将获得CfooC

因此,一个字符一个字符的方法是最安全的:

  • 查看每行中从索引0开始的每个位置
  • 如果字符串中的任何键在此时都匹配,请替换为该键的替代物
  • 增加索引并重复

Tcl语言使用其string map命令来完成此操作。这是一个bash实现:

# read fileB into an associative array
# keep track of the keys separately so we can be sure to process them in order
declare -A replacements
declare -a keys

while read -r key value; do
    replacements[$key]=$value
    keys+=("$key")
done < fileB

# process fileA
while IFS= read -r line; do
    new=""
    i=0
    while (( i < ${#line} )); do
        replaced=false
        for key in "${keys[@]}"; do
            len=${#key}
            if [[ ${line:i:len} == "$key" ]]; then
                new+=${replacements[$key]}
                replaced=true
                (( i += len ))
                break
            fi
        done
        # did we find a replacement at this point in the string?
        # if not, append the character to the new string.
        if ! $replaced; then
            new+=${line:i:1}
            (( i += 1 ))
        fi
    done
    echo "$new"
done < fileA

导致

(((foo,bar,foox,barn,foon),((cat,dog,sheep,abc),((cadd,dget,vdhfu,dssu,dfhty,dueit),(cdfte,shdgt,cdht,ddht,ddh)))),((cdc,addge)));
(((foo,bar,foox,barn,foon),(cat,dog,sheep,abc)),(cadd,dget,vdhfu,dssu,dfhty,dueit)),(cdfte,shdgt,cdht,ddht,ddh)),((cdc,addge)));

答案 3 :(得分:0)

调试bash内容的一种好方法是将其回显:

while read n k; do echo sed -i.bak "s/$k/$n/g" fileA; done < fileB

导致:

sed -i.bak s/(foo,bar,foox,barn,foon)/A/g fileA
sed -i.bak s/(cat,dog,sheep,abc)/B/g fileA
sed -i.bak s/(cadd,dget,vdhfu,dssu,dfhty,dueit)/C/g fileA
sed -i.bak s/(cdfte,shdgt,cdht,ddht,ddh)/D/g fileA
sed -i.bak s/( cdc,addge)/E/g fileA

可以肯定,您打算将A替换为(foo,bar,foox,barn,foon),而不是相反-这就是您所写的内容。

while read n k; do sed -i.bak "s/$n/$k/g" fileA; done < fileB

似乎更像它。

答案 4 :(得分:0)

能否请您尝试以下操作,仅在GNU awk上进行测试。

awk 'FNR==NR{a[$1]=$2;next} {for(i=1;i<=NF;i++){$i=a[$i]?a[$i]:$i}} 1' FS=" "  Input_fileB  FS="" OFS=  Input_fileA

输出如下。

(((foo,bar,foox,barn,foon),((cat,dog,sheep,abc),((cadd,dget,vdhfu,dssu,dfhty,dueit),(cdfte,shdgt,cdht,ddht,ddh)))),((cdc,addge)));  
(((foo,bar,foox,barn,foon),(cat,dog,sheep,abc)),(cadd,dget,vdhfu,dssu,dfhty,dueit)),(cdfte,shdgt,cdht,ddht,ddh)),((cdc,addge)));

答案 5 :(得分:0)

您还可以将fileB行转换为替换命令(如@KamilCuk的答案所示),并通过将process substitution与sed -f标志一起使用来将输出处理为文件:

sed -f <(sed -E 's#([^ ]*) (.*)#s/\1/\2/#' fileB) fileA