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

Bash 如何用不同的模式替换模式列表?,bash,awk,sed,Bash,Awk,Sed,我尝试使用sed或awk更改文件中的一些单词 我有一个像这样的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) 我想用模式文件中的值

我尝试使用
sed
awk
更改文件中的一些单词

我有一个像这样的
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) 
我想用模式文件中的值替换
A
B
C
D
E
中的值


我的尝试:

while read n k; do sed -i.bak "s/$k/$n/g" fileA; done < fileB
读取n k时
;do sed-i.bak“s/$k/$n/g”文件a;完成
内部的
sed
fileB
中的行转换为
s///g
。 然后运行下一个sed,参数作为第一个sed的输出

对于输入文件
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
可以将其传递到外部
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

您也可以尝试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)));

$

在这种情况下需要担心的一点是,其中一个替换值是否包含一个replacment键。例如,如果您正在查看原始文本

AfooB
并替换为

A B
B C
您希望以
BfooC
结束,但如果您连续进行全文重播:

sed -i 's/A/B/g' file
sed -i 's/B/C/g' file
您将获得
CfooC

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

  • 从索引0开始查看每行中的每个位置
  • 如果字符串中的任何键在此点匹配,请替换为该键的替换项
  • 增加索引并重复
Tcl语言通过其命令来实现这一点。下面是一个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

请尝试以下内容,仅使用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

您还可以将
fileB
行转换为替换命令(如@KamilCuk的回答中所建议),并使用sed
-f
标志将输出作为文件处理:

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

sed-f出了什么问题?请在您的问题中输入错误/不正确的结果。没有更改,文件是相同的。我还尝试了
sed“s/$k/$n/g”
sed“s/“$k”/“$n”/g”
也许sed不是最好的工具?将替换哪一个?整个列表,例如(foo、bar等)或任何一个元素?整个列表。可以是很长的一些名单。格式是
(Abcd_xyz_x1,Efgh_abcs_y2,Mnho_kjhu_b2)
里面没有空格。很高兴听到这个。。祝你今天愉快@Nico64,你能检查一下这个然后告诉我吗?
(((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)));
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)));
sed -f <(sed -E 's#([^ ]*) (.*)#s/\1/\2/#' fileB) fileA