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

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

т.е.

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

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

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

13 ответов

Решение

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

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

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

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

sed -e '1,/TERMINATE/d'

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

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

Если вы хотите строки раньше TERMINATE:

sed -e '/TERMINATE/,$d'

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

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

Файлы before и after будут содержать строку с terminate, поэтому для обработки каждого из них вам необходимо использовать:

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,

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

grep -A100000 TERMINATE file

какие greps для TERMINATE и выводит до 100000 строк, следующих за этой строкой.

С man страницы

-A NUM, --after-context=NUM

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

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

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

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

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

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

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

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

Объяснение:

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

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


Обобщение:

  • У вас есть файл с начальной и конечной линиями, и вы хотите, чтобы линии между этими строками исключали начальную и конечную строки.
  • начало- и конец строки могут быть определены с помощью регулярного выражения, соответствующего строке.

Пример:

$ 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. Если найдена конечная строка, печать не производится. Обратите внимание, что эта проверка выполняется перед фактической печатью, чтобы исключить конечную строку из результата.
  2. Напечатать текущую строку, если found установлено.
  3. Если начальная строка найдена, установите found=1 так что будут напечатаны следующие строки. Обратите внимание, что эта проверка выполняется после фактической печати, чтобы исключить начальную строку из результата.

Заметки:

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

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

content=$(cat file)
echo "${content#*TERMINATE}"

grep -A 10000000 'TERMINATE' файл

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

Есть много способов сделать это с 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

Если по какой-либо причине вы хотите избежать использования 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

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

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

В моей команде bash я ищу несколько строк отметок в текстовом файле log.txt . Моя отметка #mark1678793202693. Эта отметка встречается в текстовом файле 2 раза. Я хочу всегда печатать блок между двумя одинаковыми отметками.

$a содержит все количество строк в текстовом файле log.txt.

$aro — переменная преобразования $a в массив

$s — начальная строка — первый найденный шаблон в строке

$e — конечная строка — второй шаблон в log.txt

Теперь вы можете использовать sed и печатать строку от lineNumber до lineNumber.

      a=$(awk '/#mark1678793202693/{print NR}' log.txt) ; aro=($a),s=${aro[0]};e=${aro[1]} ; sed -n -e "${s}","${e}p" log.txt

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

Файл grep -A466 'TERMINATE'

Они будут печатать все строки от последней найденной строки "TERMINATE" до конца файла:

LINE_NUMBER=`grep -o -n TERMINATE $OSCAM_LOG|tail -n 1|sed "s/:/ \\'/g"|awk -F" " '{print $1}'`
tail -n +$LINE_NUMBER $YOUR_FILE_NAME

sed - намного лучший инструмент для работы: файл sed -n '/re/,$p'

где re это регулярное выражение

Другой вариант - флаг grep --after-context. Вам нужно ввести число, чтобы закончить на этом, использование wc в файле должно дать правильное значение для остановки. Объедините это с -n и выражением вашего соответствия.

Другие вопросы по тегам