Как сделать замену текста между файлами с помощью awk

#awk

#awk

Вопрос:

Я пытаюсь использовать awk для того, чтобы делать определенные замены в текстовом файле на основе значений, взятых из другого файла. Более подробно и на простых примерах у меня есть необработанный файл A.txt это должно быть обработано:

 000  y4fy      1  0h0l
000  rft5      1  yrt3
000  g34y      1  ht74
000  ll90      2  t964
000  ew3x      2  472e
000  jo7e      3  6rhf
000  f5gg      4  gs84
000  wp5y      5  6rru
000  3em1      6  c2cn
000  pti1      7  ldr3
000  4vhd      7  epp3
000  2kfb      8  twtv
 

и так далее…

Второй файл B.txt содержит подмножество значений поля $ 3 из A.txt:

 2
5
7
 

Я пытаюсь сделать конкретные замены в поле $ 4 A.txt
на основе значений B.txt .

Теперь мы можем выделить два разных случая.

Case_1: допустим, текст susbstitution представляет собой одну строку TTTT , и вывод должен быть:

 000  y4fy      1  0h0l
000  rft5      1  yrt3
000  g34y      1  ht74
000  ll90      2  TTTT
000  ew3x      2  TTTT
000  jo7e      3  6rhf
000  f5gg      4  gs84
000  wp5y      5  TTTT
000  3em1      6  c2cn
000  pti1      7  TTTT
000  4vhd      7  TTTT
000  2kfb      8  twtv
 

в котором формат сохраняется.

Случай_2: текст susbstitution связан с определенным значением в B.txt , такие как:

 2   HHHH
5   AAAA
7   MMMM
 

и результат должен быть:

 000  y4fy      1  0h0l
000  rft5      1  yrt3
000  g34y      1  ht74
000  ll90      2  HHHH
000  ew3x      2  HHHH
000  jo7e      3  6rhf
000  f5gg      4  gs84
000  wp5y      5  AAAA
000  3em1      6  c2cn
000  pti1      7  MMMM
000  4vhd      7  MMMM
000  2kfb      8  twtv
 

В принципе, мне кажется, что case_1 является конкретным экземпляром case_2.

Поскольку такой процесс включает в себя сотни очень длинных файлов, я пытаюсь найти решение, которое использует awk вместо более медленного цикла чтения во время чтения в bash.

Через пару дней лучший код, который я получил для case_1:

 while read val; do awk -v x="$val" '{if ($1 == "0000" amp;amp; $3 == x) $4 = "TTTT" ; else print $0}' A.txt; done < B.txt > res.txt
 

что, конечно, не работает.
Интересно, может ли кто-нибудь предложить решение для case_1 и,
надеюсь, для case_2. Спасибо.

Ответ №1:

Вы можете использовать:

Случай 1:

 cat file1
2
5
7

cat file2
000  y4fy      1  0h0l
000  rft5      1  yrt3
000  g34y      1  ht74
000  ll90      2  t964
000  ew3x      2  472e
000  jo7e      3  6rhf
000  f5gg      4  gs84
000  wp5y      5  6rru
000  3em1      6  c2cn
000  pti1      7  ldr3
000  4vhd      7  epp3
000  2kfb      8  twtv
 

Затем используйте awk как:

 awk 'NR == FNR {map[$1] = $2; next} $3 in map {s = (map[$3] == "" ? "TTTT" : map[$3]); sub(/[^[:blank:]] $/, s)} 1' file1 file2

000  y4fy  1  HHHH
000  rft5  1  HHHH
000  g34y  1  HHHH
000  ll90  2  TTTT
000  ew3x  2  TTTT
000  jo7e  3  AAAA
000  f5gg  4  gs84
000  wp5y  5  TTTT
000  3em1  6  c2cn
000  pti1  7  TTTT
000  4vhd  7  TTTT
000  2kfb  8  MMMM
 

Случай 2:

 cat file1

2   HHHH
5   AAAA
7   MMMM
 

Затем запустите его как:

 awk 'NR == FNR {map[$1] = $2; next} $3 in map {s = (map[$3] == "" ? "TTTT" : map[$3]); sub(/[^[:blank:]] $/, s)} 1' file1 file2

000  y4fy  1  0h0l
000  rft5  1  yrt3
000  g34y  1  ht74
000  ll90  2  HHHH
000  ew3x  2  HHHH
000  jo7e  3  6rhf
000  f5gg  4  gs84
000  wp5y  5  AAAA
000  3em1  6  c2cn
000  pti1  7  MMMM
000  4vhd  7  MMMM
000  2kfb  8  twtv
 

Чтобы сделать его более читаемым:

 awk 'NR == FNR {  # while processing first file
   map[$1] = $2   # store $2 in an associative array with key as $1
   next
}
$3 in map {       # if $3 is found in associative array
   # set $4=TTTT when value is empty otherwise use value stored in array
   s = (map[$3] == "" ? "TTTT" : map[$3])
   sub(/[^[:blank:]] $/, s)
} 1' file1 fil22
 

Комментарии:

1. выходной файл, который вы получаете, не соответствует ожиданиям, и я опубликовал его. Ваш входной файл (file1) отличается от моего (B.txt ). Вы как бы перепутали эти два дела.

2. Я объединил оба случая в один входной файл, чтобы одна и та же команда awk могла работать для обоих случаев. Разве это не работает?

3. просто сравните мои ожидаемые результаты для двух случаев с вашими, и вы поймете, что это не работает.

4. большое спасибо за ваш код, он работает нормально. Не могли бы вы, пожалуйста, объяснить код, в частности, как и почему вы использовали функцию map?

5. map это не функция. Это просто имя массива. Это тоже может быть что угодно подобное arr . Я добавил объяснение.