Как получить часть файла после строки, которая соответствует выражению grep? (первый матч) - PullRequest
156 голосов
/ 18 августа 2011

У меня есть файл с около 1000 строк.Мне нужна часть моего файла после строки, которая соответствует моему выражению grep.

т.е.

$ cat file | grep 'TERMINATE'     // Its found on line 534

Итак, я хочу файл из строки 535 to line 1000 для дальнейшей обработки.

Как мне это сделать?

Ответы [ 12 ]

282 голосов
/ 18 августа 2011

Следующая строка напечатает строку, соответствующую TERMINATE до конца файла:

sed -n -e '/TERMINATE/,$p'

Объяснено: -n отключает поведение по умолчанию sed печати каждой строки после выполнения на ней своего сценария, -e указывает сценарий на sed, /TERMINATE/,$ является адресом ( line) выбор диапазона означает, что первая строка соответствует регулярному выражению TERMINATE (например, grep) и концу файла ($), а p - команда печати, которая печатает текущую строку.

Будет выведено из строки, следующей за строкой, соответствующей TERMINATE, до конца файла:
(от ПОСЛЕ совпадающей строки до EOF, НЕ включая совпадающую строку)

sed -e '1,/TERMINATE/d'

Объяснено: 1,/TERMINATE/ - выбор диапазона адресов (строк), означающий, что первая строка для ввода в 1-ю строку соответствует регулярному выражению TERMINATE, а d - команда удаления, которая удалить текущую строку и перейти к следующей строке. Так как sed поведение по умолчанию - печать строк, он будет печатать строки после TERMINATE до конца ввода.

Edit:

Если вы хотите строки до TERMINATE:

sed -e '/TERMINATE/,$d'

А если вам нужны обе строки до и после TERMINATE в 2 разных файлах за один проход:

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file

В файлах до и после будет строка с завершением, поэтому для обработки каждого из них вам необходимо использовать:

head -n -1 before
tail -n +2 after

Edit2:

Если вы не хотите жестко кодировать имена файлов в сценарии sed, вы можете:

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file

Но тогда вам нужно экранировать $, означающее последнюю строку, чтобы оболочка не пыталась расширить переменную $w (обратите внимание, что теперь мы используем двойные кавычки вокруг скрипта вместо одинарных кавычек).

Я забыл сказать, что новая строка важна после имен файлов в скрипте, чтобы sed знал, что имена файлов заканчиваются.


Редактировать: 2016-0530

Себастьян Клеман спросил: «Как бы вы заменили жестко закодированный TERMINATE на переменную?»

Вы должны создать переменную для соответствующего текста, а затем сделать это так же, как в предыдущем примере:

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file

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

## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the 
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"

Важными моментами при замене текста переменными в этих случаях являются:

  1. Переменные ($variablename), заключенные в single quotes ['], не будут "расширяться", но будут переменные внутри double quotes ["]. Таким образом, вы должны изменить все single quotes на double quotes, если они содержат текст, который вы хотите заменить на переменную.
  2. Диапазоны sed также содержат $, за которыми сразу следует буква, такая как: $p, $d, $w. Они также будут выглядеть как переменные, которые нужно раскрыть, поэтому вам нужно экранировать эти $ символы с обратной косой чертой [\], например: \$p, \$d, \$w.
61 голосов
/ 18 августа 2011

В качестве простого приближения вы можете использовать

grep -A100000 TERMINATE file

, который показывает TERMINATE и выводит до 100000 строк после этой строки.

Со страницы man

-A NUM, --after-context=NUM

Печать NUM строк конечного контекста после сопоставления строк. Помещает строку, содержащую разделитель групп (-), между смежными группами совпадений.С опцией -o или --only-match это не имеет никакого эффекта, и выдается предупреждение.

26 голосов
/ 18 апреля 2013

Инструмент для использования здесь: awk:

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'

Как это работает:

  1. Мы устанавливаем переменную 'found' в ноль, оценивая false
  2. если совпадение для 'TERMINATE' найдено с регулярным выражением, мы устанавливаем его равным единице.
  3. Если наша переменная 'found' имеет значение True, print:)

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

8 голосов
/ 29 июня 2015

Если я правильно понимаю ваш вопрос, вам нужны строки после TERMINATE, не включая TERMINATE -линию. awk может сделать это простым способом:

awk '{if(found) print} /TERMINATE/{found=1}' your_file

Пояснение:

  1. Хотя это не лучшая практика, вы можете полагаться на тот факт, что все переменные по умолчанию равны 0 или пустая строка, если она не определена. Поэтому первое выражение (if(found) print) не будет печатать ничего, с чего можно начать.
  2. После того, как печать завершена, мы проверяем, является ли это стартовой линией (которая не должна быть включена).

Это напечатает все строки после строки TERMINATE.


Обобщение:

  • У вас есть файл с начала - и конца -линии, и вы хотите, чтобы линии между этими строками исключали начала - и конец -линии.
  • start - и end -line могут быть определены регулярным выражением, совпадающим со строкой.
* +1047 * Пример: * +1048 *
$ cat ex_file.txt 
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt 
A good line to include
And this line
Yep
$

Пояснение:

  1. Если найдена строка end , печать не производится. Обратите внимание, что эта проверка выполняется за до фактической печати, чтобы исключить строку end из результата.
  2. Распечатать текущую строку, если установлено found.
  3. Если найдена строка start , установите found=1 так, чтобы были напечатаны следующие строки. Обратите внимание, что эта проверка выполняется после фактической печати, чтобы исключить строку start из результата.

Примечания:

  • Код основан на том факте, что по умолчанию все awk-vars имеют значение 0 или пустую строку, если она не определена. Это верно, но не может быть лучшей практикой, поэтому вы можете добавить BEGIN{found=0} в начало выражения awk.
  • Если найдено несколько начальных-конечных -блоков, все они печатаются.
7 голосов
/ 18 августа 2011

Используйте расширение параметра bash следующим образом:

content=$(cat file)
echo "${content#*TERMINATE}"
4 голосов
/ 09 ноября 2017

grep -A 10000000 'TERMINATE' file

  • намного, намного быстрее, чем sed, особенно при работе с действительно большими файлами.Он работает до 10 миллионов строк (или что-то, что вы вставляете), так что нет ничего страшного в том, чтобы сделать его достаточно большим, чтобы справиться со всем, что вы нажали.
3 голосов
/ 31 июля 2014

Есть много способов сделать это с sed или awk:

sed -n '/TERMINATE/,$p' file

Это ищет TERMINATE в вашем файле и печатает от этой строки до конца файла.

awk '/TERMINATE/,0' file

Это точно такое же поведение, как и sed.

Если вам известен номер строки, с которой вы хотите начать печать, вы можете указать ее вместе с NR (номер записи, который в итоге указывает номер строки):

awk 'NR>=535' file

Пример

$ seq 10 > a        #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10
3 голосов
/ 18 августа 2011

Если по какой-либо причине вы хотите избежать использования sed, следующая строка напечатает строку, соответствующую TERMINATE до конца файла:

tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file

, а следующая строка будет напечатана из следующей строкисоответствие TERMINATE до конца файла:

tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file

Требуется 2 процесса, чтобы сделать то, что sed может сделать в одном процессе, и если файл изменяется между выполнением grep и tail, результат можетбыть бессвязным, поэтому я рекомендую использовать sed.Кроме того, если файл не содержит TERMINATE, 1-я команда не выполняется.

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

Это может быть один из способов сделать это.Если вы знаете, в какой строке файла у вас есть слово grep и сколько строк в вашем файле:

grep -A466 'TERMINATE' file

0 голосов
/ 23 июля 2016

Альтернативы отличному ответу sed от jfgagne, и которые не включают совпадающую строку:

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