it-swarm.com.ru

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

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

то есть.

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

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

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

142
Yugal Jindle

Далее будет напечатана строка, соответствующая 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.
263
jfg956

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

grep -A100000 TERMINATE file

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

С man страницы

-A NUM, --after-context=NUM

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

59
aioobe

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

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

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

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

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

25
Jos De Graeve

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

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

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

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

Объяснение:

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

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


Обобщение:

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

Пример:

$ 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.
  • Если найдено несколько блоков start-end -, все они печатаются.
7
UlfR

Если по какой-либо причине вы хотите отказаться от использования 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. Более того, если файл не содержит TERMINATE, 1-я команда не выполняется.

3
jfg956

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

grep -A 10000000 'TERMINATE' файл 

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

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

файл grep -A466 'TERMINATE'

0
Mariah

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

0
mivk