File1
<filename1.txt> stringA string1
<filename2.txt> stringA string2
<filename2.txt> stringB string3
File2
<filename1.txt> words and symbols < $ stringA words and symbols 9
<filename2.txt> more words and symbols % @ stringA words and symbols stringB
File2 Transformed
<filename1.txt> words and symbols < $ string1 words and symbols 9
<filename2.txt> more words and symbols % @ string2 words and symbols string3
Мой подход состоит в том, чтобы перебирать строки File1 для установки переменных. Итак, для строки 1 переменные устанавливаются следующим образом:
filenamevar = <filename1.txt>
string_old_var = stringA
string_new_var = string1
Затем для строк grep, которые соответствуют filenamevar и string_old_var в качестве входных данных для sed. Здесь мне нужна помощь (если вы не думаете, что есть лучший способ в целом).
Ближайшим пока является
grep -e "$filenamevar.*$string_old_var" File2.txt | sed s/$string_old_var/$string_new_var/ >> File2Transformed.txt
, который работает, за исключением того, что вторая строка выводится дважды:
<filename1.txt> words and symbols < $ string1 words and symbols 9
<filename2.txt> more words and symbols % @ string2 words and symbols stringB
<filename2.txt> more words and symbols % @ stringA words and symbols string3
Я также пробовал
grep -e "$filenamevar.*$string_old_var" File2 | sed s/$string_old_var/$string_new_var/
и
sed -i s/$string_old_var/$string_new_var/ $(grep -e "$filenamevar.*$string_old_var" File2)
, но ни один из них не дает желаемых результатов.
Вот цикл, который я использую
Numlines=$(grep "" -c File1.txt)
for (( line=1; line<=$Numlines; line++ )) ; do
filenamevar=$(awk -v line=$line 'NR == line {print $1}' File1.txt)
string_old_var=$(awk -v line=$line 'NR == line {print $2}' File1.txt)
string_new_var=$(awk -v line=$line 'NR == line {print $3}' File1.txt)
# insert proper sed grep code to test here
done
Если вы решили использовать для этого цикл оболочки, вы можете сделать что-то вроде
while read -r fname patt repl; do
sed -i.bak "/$fname/s/^$patt/$repl/" File2
done < File1
,но обратите внимание, что он может неожиданно прерваться, если какая-либо из ваших строк содержит специальные последовательности символов регулярного выражения. Другим вариантом (с тем же предупреждением о специальных символах) может быть что-то вроде
awk '{printf "/^%s/s/%s/%s/\n",$1,$2,$3}' File1 | sed -f- File2
, который использует awk
для форматирования File1 в последовательность команд, которые затем передаются в sed -f
для изменить файл2.
С помощью GNU awk (он же gawk
), который поддерживает двумерные массивы, вы можете просто сделать:
$ gawk '
NR==FNR {a[$1][$2] = $3; next}
$1 in a {for(i=2;i<=NF;i++) $i = $i in a[$1] ? a[$1][$i] : $i}
1
' File1 File2
<filename1.txt> words and symbols < $ string1 words and symbols 9
<filename2.txt> more words and symbols % @ string2 words and symbols string3
или, если вам нужно сохранить выравнивание, используйте функцию index
с substr
:
$ gawk '
NR==FNR {a[$1][$2] = $3; next}
$1 in a {
for(s in a[$1]) {
mstart = index($0,s);
if(mstart > 0) $0 = substr($0,1,mstart-1) a[$1][s] substr($0,mstart+length(s))}
}
1
' File1 File2
<filename1.txt> words and symbols < $ string1 words and symbols 9
<filename2.txt> more words and symbols % @ string2 words and symbols string3
Похожая вещь в vanilla awk, подделка 2D-массива с помощью строки с разделителями FS:
mawk '
NR==FNR {a[$1 FS $2] = $3; next}
{
for(k in a) {
split(k,b);
if($1 == b[1]) {
mstart = index($0,b[2]);
if(mstart > 0) $0 = substr($0,1,mstart-1) a[k] substr($0,mstart+length(b[2]));
}
}
}
1
' File1 File2