Удалить n1 предыдущие строки и n2 строки, следующие за строкой, содержащей шаблон - PullRequest
8 голосов
/ 25 февраля 2012
sed -e '/XXXX/,+4d' fv.out

Мне нужно найти определенный шаблон в файле и удалить одновременно 5 строк выше и 4 строки ниже. Я обнаружил, что строка выше удаляет строку, содержащую шаблон и четыре строки под ним.

sed -e '/XXXX/,~5d' fv.out

В руководстве sed было указано, что ~ представляет линии, за которыми следует шаблон. Но когда я попробовал, это были строки, следующие шаблону, который был удален.

Итак, как мне удалить 5 строк выше и 4 строки ниже строки, содержащей шаблон одновременно?

Ответы [ 5 ]

5 голосов
/ 25 февраля 2012

В одну сторону, используя sed, предполагая, что шаблоны не достаточно близки друг к другу:

Содержимое script.sed:

## If line doesn't match the pattern...
/pattern/ ! { 

    ## Append line to 'hold space'.
    H   

    ## Copy content of 'hold space' to 'pattern space' to work with it.
    g   

    ## If there are more than 5 lines saved, print and remove the first
    ## one. It's like a FIFO.
    /\(\n[^\n]*\)\{6\}/ {

        ## Delete the first '\n' automatically added by previous 'H' command.
        s/^\n//
        ## Print until first '\n'.
        P   
        ## Delete data printed just before.
        s/[^\n]*//
        ## Save updated content to 'hold space'.
        h   
    } 

### Added to fix an error pointed out by potong in comments.
### =======================================================
    ## If last line, print lines left in 'hold space'.
    $ { 
        x   
        s/^\n//
        p   
    } 
### =======================================================


    ## Read next line.
    b   
}

## If line matches the pattern...
/pattern/ {

    ## Remove all content of 'hold space'. It has the five previous
    ## lines, which won't be printed.
    x   
    s/^.*$//
    x   

    ## Read next four lines and append them to 'pattern space'.
    N ; N ; N ; N 

    ## Delete all.
    s/^.*$//
}

Беги как:

sed -nf script.sed infile
2 голосов
/ 25 февраля 2012

Решение с использованием awk:

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; }
     nlines == 5 { print lines[NR%5]; nlines-- }
     lines2del == 0 { lines[NR%5] = $0; nlines++ }
     lines2del > 0 { lines2del-- }
     END { while (nlines-- > 0)  { print lines[(NR - nlines) % 5] } }' fv.out

Обновление:

Это сценарий объяснил:

  • Я помню последние 5 строк в массиве lines с использованием вращательных индексов (NR% 5; NR - номер записи; в данном случае строки).
  • Если я найду шаблон в текущей строке ($0 ~ "XXXX; $0 - текущая запись: в данном случае строка; и ~ - оператор сопоставления Extended Regular Expression ), Я сбросил количество прочитанных строк и заметил, что у меня есть 5 строк для удаления (включая текущую строку).
  • Если я уже прочитал 5 строк, я печатаю текущую строку.
  • Если у меня нет строк для удаления (что также верно, если я прочитал 5 строк, я помещаю текущую строку в буфер и увеличиваю количество строк. Обратите внимание, как число строк уменьшается, а затем увеличивается, если строка напечатана.
  • Если нужно удалить строки, я ничего не печатаю и уменьшаю количество удаляемых строк.
  • В конце скрипта я печатаю все строки в массиве.

Моя оригинальная версия скрипта была следующей, но в итоге я оптимизировал ее до вышеуказанной версии:

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; }
     lines2del == 0 && nlines == 5 { print lines[NR%5]; lines[NR%5] }
     lines2del == 0 && nlines < 5 { lines[NR%5] = $0; nlines++ }
     lines2del > 0 { lines2del-- }
     END { while (nlines-- > 0)  { print lines[(NR - nlines) % 5] } }' fv.out

awk отличный инструмент! Я настоятельно рекомендую вам найти учебник в сети и прочитать его. Одна важная вещь: awk работает с Расширенные регулярные выражения ( ERE ). Их синтаксис немного отличается от Стандартного регулярного выражения ( RE ), используемого в sed, но все, что можно сделать с RE, можно сделать с помощью ERE.

1 голос
/ 13 сентября 2013

Если вы готовы вывести результат в файл вместо стандартного вывода, vim может сделать это довольно эффективно:

vim -c 'g/pattern/-5,+4d' -c 'w! outfile|q!' infile

или

vim -c 'g/pattern/-5,+4d' -c 'x' infile

для редактирования файлана месте.

1 голос
/ 25 февраля 2012

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

sed 'H;$!d;g;s/\([^\n]*\n\)\{5\}[^\n]*PATTERN\([^\n]*\n\)\{5\}//g;s/.//' file

или вот это:

awk --posix -vORS='' -vRS='([^\n]*\n){5}[^\n]*PATTERN([^\n]*\n){5}' 1 file

более эффективное решение sed:

sed ':a;/PATTERN/,+4d;/\([^\n]*\n\)\{5\}/{P;D};$q;N;ba' file
1 голос
/ 25 февраля 2012

Идея состоит в том, чтобы прочитать 5 строк, не печатая их. Если вы найдете шаблон, удалите незапечатанные линии и 4 строки ниже. Если вы не нашли шаблон, запомните текущую строку и напечатайте 1-ю незапечатанную строку. В конце напечатайте то, что не напечатано.

sed -n -e '/XXXX/,+4{x;s/.*//;x;d}' -e '1,5H' -e '6,${H;g;s/\n//;P;s/[^\n]*//;h}' -e '${g;s/\n//;p;d}' fv.out

Конечно, это работает только в том случае, если у вас есть один экземпляр вашего шаблона в файле. Если у вас их много, вам нужно прочитать 5 новых строк после нахождения вашего паттерна, и это будет сложно, если у вас снова будет паттерн в этих строках. В этом случае, я думаю, что sed не правильный инструмент.

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