поиск совпадения с последующей заменой значения после этого совпадения значением из предыдущей строки - PullRequest
5 голосов
/ 06 октября 2019

У меня есть несколько файлов, которые содержат тысячи строк следующего шаблона:

O   HOH     1      11.700 -11.906   1.533
H   HOH     0      12.561 -12.318   1.596
H   HOH     0      11.445 -12.031   0.627
O   HOH     2      17.897   8.999  -0.104
H   HOH     0      18.444   8.521   0.516
H   HOH     0      17.127   8.457  -0.223
O   HOH     3      -0.688  10.310 -15.189
H   HOH     0      -0.760   9.366 -15.055
H   HOH     0      -0.494  10.665 -14.324
O   HOH     4     -10.418   3.733   4.069
H   HOH     0      -9.928   3.726   4.897
H   HOH     0     -11.222   4.218   4.268
O   HOH     5       2.655  -8.910  13.338
H   HOH     0       2.377  -9.739  13.737
H   HOH     0       1.847  -8.531  12.992
O   HOH     6      -0.191  -1.010 -19.469
H   HOH     0       0.455  -1.460 -18.924
H   HOH     0      -0.571  -0.343 -18.895
O   HOH     7      14.338 -15.985  -1.467
H   HOH     0      15.129 -15.647  -1.886
H   HOH     0      13.854 -16.408  -2.175
O   HOH     8     -15.510  -7.073   1.857
H   HOH     0     -15.526  -6.116   1.874
H   HOH     0     -16.341  -7.321   1.464
O   HOH     9       9.155   6.561  -7.729
H   HOH     0       9.419   7.335  -7.245
H   HOH     0       9.943   6.018  -7.768
.
.
.
.
O   HOH  999999     9.155   6.561  -7.729
H   HOH     0       9.419   7.335  -7.245
H   HOH     0       9.943   6.018  -7.768

мне нужно заменить каждые 2 последовательных момента 'H HOH 0' на 'H HOH xxx', где xxx - этоЗначение прямо над 0s

я попытался сделать так: я разбил большой файл на 3-строчные файлы, используя

split -l 3 foo

, а затем с помощью awk скопировал необходимое значение в другой файл

awk 'NR==1 print $3' > foo--

затем попытался использовать sed для замены шаблона, прочитав файл foo-- и заменив '0' в foo

sed -e '/  0  /{r foo--' -e 'd}' foo 

, но это не сработало

уверен, что есть лучший способ сделать это

Конечный результат должен выглядеть следующим образом:

O   HOH     1      11.700 -11.906   1.533
H   HOH     1      12.561 -12.318   1.596
H   HOH     1      11.445 -12.031   0.627
O   HOH     2      17.897   8.999  -0.104
H   HOH     2      18.444   8.521   0.516
H   HOH     2      17.127   8.457  -0.223
O   HOH     3      -0.688  10.310 -15.189
H   HOH     3      -0.760   9.366 -15.055
H   HOH     3      -0.494  10.665 -14.324
O   HOH     4     -10.418   3.733   4.069
H   HOH     4      -9.928   3.726   4.897
H   HOH     4     -11.222   4.218   4.268
O   HOH     5       2.655  -8.910  13.338
H   HOH     5       2.377  -9.739  13.737
H   HOH     5       1.847  -8.531  12.992
O   HOH     6      -0.191  -1.010 -19.469
H   HOH     6       0.455  -1.460 -18.924
H   HOH     6      -0.571  -0.343 -18.895
.
.
.
O   HOH     99999      -0.191  -1.010 -19.469
H   HOH     99999       0.455  -1.460 -18.924
H   HOH     99999      -0.571  -0.343 -18.895

Ответы [ 5 ]

2 голосов
/ 06 октября 2019

Чтобы получить только те значения, которые вам нужны, если вас не интересует расстояние между полями:

$ awk '$1=="H"{$3=p} {p=$3} 1' file
O   HOH     1      11.700 -11.906   1.533
H HOH 1 12.561 -12.318 1.596
H HOH 1 11.445 -12.031 0.627
O   HOH     2      17.897   8.999  -0.104
H HOH 2 18.444 8.521 0.516
H HOH 2 17.127 8.457 -0.223
O   HOH     3      -0.688  10.310 -15.189
H HOH 3 -0.760 9.366 -15.055
H HOH 3 -0.494 10.665 -14.324
O   HOH     4     -10.418   3.733   4.069
H HOH 4 -9.928 3.726 4.897
H HOH 4 -11.222 4.218 4.268
O   HOH     5       2.655  -8.910  13.338
H HOH 5 2.377 -9.739 13.737
H HOH 5 1.847 -8.531 12.992
O   HOH     6      -0.191  -1.010 -19.469
H HOH 6 0.455 -1.460 -18.924
H HOH 6 -0.571 -0.343 -18.895
O   HOH     7      14.338 -15.985  -1.467
H HOH 7 15.129 -15.647 -1.886
H HOH 7 13.854 -16.408 -2.175
O   HOH     8     -15.510  -7.073   1.857
H HOH 8 -15.526 -6.116 1.874
H HOH 8 -16.341 -7.321 1.464
O   HOH     9       9.155   6.561  -7.729
H HOH 9 9.419 7.335 -7.245
H HOH 9 9.943 6.018 -7.768
O   HOH  999999     9.155   6.561  -7.729
H HOH 999999 9.419 7.335 -7.245
H HOH 999999 9.943 6.018 -7.768

или разделенные пробелом последние 4 поля, выровненные по правому краю:

$ awk '$1=="H"{$3=p} {p=$3} 1' file | column -t -R3,4,5,6
O  HOH       1   11.700  -11.906    1.533
H  HOH       1   12.561  -12.318    1.596
H  HOH       1   11.445  -12.031    0.627
O  HOH       2   17.897    8.999   -0.104
H  HOH       2   18.444    8.521    0.516
H  HOH       2   17.127    8.457   -0.223
O  HOH       3   -0.688   10.310  -15.189
H  HOH       3   -0.760    9.366  -15.055
H  HOH       3   -0.494   10.665  -14.324
O  HOH       4  -10.418    3.733    4.069
H  HOH       4   -9.928    3.726    4.897
H  HOH       4  -11.222    4.218    4.268
O  HOH       5    2.655   -8.910   13.338
H  HOH       5    2.377   -9.739   13.737
H  HOH       5    1.847   -8.531   12.992
O  HOH       6   -0.191   -1.010  -19.469
H  HOH       6    0.455   -1.460  -18.924
H  HOH       6   -0.571   -0.343  -18.895
O  HOH       7   14.338  -15.985   -1.467
H  HOH       7   15.129  -15.647   -1.886
H  HOH       7   13.854  -16.408   -2.175
O  HOH       8  -15.510   -7.073    1.857
H  HOH       8  -15.526   -6.116    1.874
H  HOH       8  -16.341   -7.321    1.464
O  HOH       9    9.155    6.561   -7.729
H  HOH       9    9.419    7.335   -7.245
H  HOH       9    9.943    6.018   -7.768
O  HOH  999999    9.155    6.561   -7.729
H  HOH  999999    9.419    7.335   -7.245
H  HOH  999999    9.943    6.018   -7.768

или в формате TSV:

$ awk -v OFS='\t' '{$3=($1=="H" ? p : $3); p=$3} 1' file
O       HOH     1       11.700  -11.906 1.533
H       HOH     1       12.561  -12.318 1.596
H       HOH     1       11.445  -12.031 0.627
O       HOH     2       17.897  8.999   -0.104
H       HOH     2       18.444  8.521   0.516
H       HOH     2       17.127  8.457   -0.223
O       HOH     3       -0.688  10.310  -15.189
H       HOH     3       -0.760  9.366   -15.055
H       HOH     3       -0.494  10.665  -14.324
O       HOH     4       -10.418 3.733   4.069
H       HOH     4       -9.928  3.726   4.897
H       HOH     4       -11.222 4.218   4.268
O       HOH     5       2.655   -8.910  13.338
H       HOH     5       2.377   -9.739  13.737
H       HOH     5       1.847   -8.531  12.992
O       HOH     6       -0.191  -1.010  -19.469
H       HOH     6       0.455   -1.460  -18.924
H       HOH     6       -0.571  -0.343  -18.895
O       HOH     7       14.338  -15.985 -1.467
H       HOH     7       15.129  -15.647 -1.886
H       HOH     7       13.854  -16.408 -2.175
O       HOH     8       -15.510 -7.073  1.857
H       HOH     8       -15.526 -6.116  1.874
H       HOH     8       -16.341 -7.321  1.464
O       HOH     9       9.155   6.561   -7.729
H       HOH     9       9.419   7.335   -7.245
H       HOH     9       9.943   6.018   -7.768
O       HOH     999999  9.155   6.561   -7.729
H       HOH     999999  9.419   7.335   -7.245
H       HOH     999999  9.943   6.018   -7.768

Если вам необходимо сохранить исходный интервал, тогда с GNU awk для 4-го аргумента для split () это сохранит любой интервал, с которого вы начали:

$ cat tst.awk
{
    if ($1 == "H") {
        split(prev,pflds,FS,pseps)
        n = split($0,flds,FS,seps)
        seps[2] = pseps[2]
        flds[3] = pflds[3]
        seps[3] = sprintf("%*s",length(pseps[3]) + length(pflds[4]) - length(flds[4]),"")
        $0 = ""
        for (i=1; i<=n; i++) {
            $0 = $0 flds[i] seps[i]
        }
    }
    print
    prev = $0
}

.

$ awk -f tst.awk file
O   HOH     1      11.700 -11.906   1.533
H   HOH     1      12.561 -12.318   1.596
H   HOH     1      11.445 -12.031   0.627
O   HOH     2      17.897   8.999  -0.104
H   HOH     2      18.444   8.521   0.516
H   HOH     2      17.127   8.457  -0.223
O   HOH     3      -0.688  10.310 -15.189
H   HOH     3      -0.760   9.366 -15.055
H   HOH     3      -0.494  10.665 -14.324
O   HOH     4     -10.418   3.733   4.069
H   HOH     4      -9.928   3.726   4.897
H   HOH     4     -11.222   4.218   4.268
O   HOH     5       2.655  -8.910  13.338
H   HOH     5       2.377  -9.739  13.737
H   HOH     5       1.847  -8.531  12.992
O   HOH     6      -0.191  -1.010 -19.469
H   HOH     6       0.455  -1.460 -18.924
H   HOH     6      -0.571  -0.343 -18.895
O   HOH     7      14.338 -15.985  -1.467
H   HOH     7      15.129 -15.647  -1.886
H   HOH     7      13.854 -16.408  -2.175
O   HOH     8     -15.510  -7.073   1.857
H   HOH     8     -15.526  -6.116   1.874
H   HOH     8     -16.341  -7.321   1.464
O   HOH     9       9.155   6.561  -7.729
H   HOH     9       9.419   7.335  -7.245
H   HOH     9       9.943   6.018  -7.768
O   HOH  999999     9.155   6.561  -7.729
H   HOH  999999     9.419   7.335  -7.245
H   HOH  999999     9.943   6.018  -7.768
2 голосов
/ 06 октября 2019

Вы можете делать все, что вам нужно, в awk, но для правильного и аккуратного форматирования потребуется использовать FIELDWIDTHS и fprinf. Проблема связана с тем, что awk убирает начальные и конечные пробелы , когда требуется перерасчет записи, например, когда запись изменяется. Неизменные записи не изменяются таким образом. Это создает проблему при предоставлении табличного вывода.

Чтобы использовать FIELDWIDTHS каждого поля, предоставьте список ширин в правиле BEGIN. Пример:

awk '
    BEGIN  { FIELDWIDTHS = "1 6 8 10 8 8" }
    $1 == "O" {n = $3}
    $1 == "H" {$3 = n}
    {printf "%s%6s%8s%10s%8s%8s\n", $1,$2,$3,$4,$5,$6}
' file

( примечание: FIELDWIDTHS - это функция GNU awk)

С вашими данными, удалив ..., вы можете простоcopy / middle-mouse-paste в командной строке для тестирования, например

$ awk '
>     BEGIN  { FIELDWIDTHS = "1 6 8 10 8 8" }
>     $1 == "O" {n = $3}
>     $1 == "H" {$3 = n}
>     {printf "%s%6s%8s%10s%8s%8s\n", $1,$2,$3,$4,$5,$6}
> ' file
O   HOH     1      11.700 -11.906   1.533
H   HOH     1      12.561 -12.318   1.596
H   HOH     1      11.445 -12.031   0.627
O   HOH     2      17.897   8.999  -0.104
H   HOH     2      18.444   8.521   0.516
H   HOH     2      17.127   8.457  -0.223
O   HOH     3      -0.688  10.310 -15.189
H   HOH     3      -0.760   9.366 -15.055
H   HOH     3      -0.494  10.665 -14.324
O   HOH     4     -10.418   3.733   4.069
H   HOH     4      -9.928   3.726   4.897
H   HOH     4     -11.222   4.218   4.268
O   HOH     5       2.655  -8.910  13.338
H   HOH     5       2.377  -9.739  13.737
H   HOH     5       1.847  -8.531  12.992
O   HOH     6      -0.191  -1.010 -19.469
H   HOH     6       0.455  -1.460 -18.924
H   HOH     6      -0.571  -0.343 -18.895
O   HOH     7      14.338 -15.985  -1.467
H   HOH     7      15.129 -15.647  -1.886
H   HOH     7      13.854 -16.408  -2.175
O   HOH     8     -15.510  -7.073   1.857
H   HOH     8     -15.526  -6.116   1.874
H   HOH     8     -16.341  -7.321   1.464
O   HOH     9       9.155   6.561  -7.729
H   HOH     9       9.419   7.335  -7.245
H   HOH     9       9.943   6.018  -7.768
O   HOH  999999     9.155   6.561  -7.729
H   HOH  999999     9.419   7.335  -7.245
H   HOH  999999     9.943   6.018  -7.768
1 голос
/ 06 октября 2019

Это может работать для вас (GNU sed):

sed -E '/^H\s+HOH\s+0\s/!{h;b};N;/\nH\s+HOH\s+0\s/{G;s/0(.*\n.[^0]*)0(.*)\n\S\s+\S+\s+(\S+).*/\3\1\3\2/};P;D' file

Если строка не начинается H HOH 0, распечатайте как обычно и сохраните копию в поле удержания. В противном случае добавьте следующую строку и, если она тоже начинается H HOH 0, добавьте копию из области удержания и, используя подстановку, переставьте первые две строки в нужный формат. Во всех остальных случаях выведите следующую строку и повторите.

Более подробно в соответствии с запросом:

  • /^H\s+HOH\s+0\s/!{h;b} Если строка не начинается H HOH 0, выведите как обычнои сохраните копию в пространстве удержания (h копирует в пространство удержания, b прерывает цикл команды sed, печатает текущую строку в пространстве образца и, если доступно, извлекает следующую строку).
  • N Текущая строка начинается H HOH 0 и добавляется следующая строка.
  • /\nH\s+HOH\s+0\s/ Добавленная строка также начинается H HOH 0, поэтому:
  • G Добавить копиюпространство удержания, то есть последняя строка, которая не начиналась H HOH 0, и:
  • s/0(.*\n.[^0]*)0(.*)\n\S\s+\S+\s+(\S+).*/\3\1\3\2/ Замените 3-е поля в обеих последних двух строках 3-м полем из копии пространства удержания.
  • P Печать до и включая первую новую строку.
  • D Удаление до и включая первую новую строку.

Последние две команды sed обслуживают регистргде строка, добавленная к пространству шаблона, не соответствует первым трем полямс второй. Это позволяет печатать первую из этих строк как обычно, а вторую обрабатывать так, как если бы эта строка была представлена ​​заново.

1 голос
/ 06 октября 2019

С GNU awk:

awk '$1=="O"{p=$3}; $1=="H" {$3=p} {printf("%s %5s      %s %10.3f %7.3f %7.3f\n",$1,$2,$3,$4,$5,$6)} ' file

Вывод:

O   HOH      1     11.700 -11.906   1.533
H   HOH      1     12.561 -12.318   1.596
H   HOH      1     11.445 -12.031   0.627
O   HOH      2     17.897   8.999  -0.104
H   HOH      2     18.444   8.521   0.516
H   HOH      2     17.127   8.457  -0.223
O   HOH      3     -0.688  10.310 -15.189
H   HOH      3     -0.760   9.366 -15.055
H   HOH      3     -0.494  10.665 -14.324
O   HOH      4    -10.418   3.733   4.069
H   HOH      4     -9.928   3.726   4.897
H   HOH      4    -11.222   4.218   4.268
O   HOH      5      2.655  -8.910  13.338
H   HOH      5      2.377  -9.739  13.737
H   HOH      5      1.847  -8.531  12.992
O   HOH      6     -0.191  -1.010 -19.469
H   HOH      6      0.455  -1.460 -18.924
H   HOH      6     -0.571  -0.343 -18.895
O   HOH      7     14.338 -15.985  -1.467
H   HOH      7     15.129 -15.647  -1.886
.
.
.
O   HOH      999999      9.155   6.561  -7.729
H   HOH      999999      9.419   7.335  -7.245
H   HOH      999999      9.943   6.018  -7.768
0 голосов
/ 06 октября 2019

Это должно работать:

awk '{ if($1=="H" && $2=="HOH" && $3=="0") $3=val; else val=$3 } 1'

В качестве альтернативы, чтобы сохранить ширину поля:

awk '{
    if($1=="H" && $2=="HOH" && $3=="0")
        sub(sprintf("0 {1,%d}", length(val)), val " ")
    else
        val=$3
} 1'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...