Как заменить список шаблонов другим? - PullRequest
0 голосов
/ 24 января 2019

Я пытаюсь изменить в файле одно слово другим, используя sed или awk.

У меня есть 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 значения A, B, C, D и E на значения в файле шаблона.


Моя попытка:

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

Ответы [ 6 ]

0 голосов
/ 25 января 2019

Вы также можете преобразовать fileB строки в команды замещения (как предложено в ответе @ KamilCuk) и обработать вывод в виде файла, используя process substitution с флагом sed -f:

sed -f <(sed -E 's#([^ ]*) (.*)#s/\1/\2/#' fileB) fileA
0 голосов
/ 25 января 2019

Не могли бы вы попробовать следующее, протестировано только с 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)));
0 голосов
/ 25 января 2019

В этой ситуации нужно беспокоиться, если одно из заменяющих значений содержит один из заменяющих ключей .Например, если вы смотрите на исходный текст

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)));
0 голосов
/ 25 января 2019

Вы также можете попробовать 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)));

$
0 голосов
/ 25 января 2019
sed "$(sed 's/^\([^ ]*\) \(.*\)$/s#\1#\2#g/' fileB)" fileA

Внутренний sed преобразует строки из fileB в s/<pattern>/<pattern>/g. Затем запускается следующий 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 для выполнения.

Проверено на jdoodle со следующим:

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

Я получаю этот вывод:

(((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))); 
0 голосов
/ 24 января 2019

Хороший способ отладить ваш 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

больше похоже на это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...