Как получить часть файла после строки, которая соответствует выражению 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"
Важными моментами о замене текста переменными в этих случаях являются:
- Переменные (
$variablename
) заключен вsingle quotes
['
] не будет "расширяться", но переменные внутриdouble quotes
["
] будут. Итак, вы должны изменить всеsingle quotes
вdouble quotes
если они содержат текст, который вы хотите заменить на переменную. 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 }'
Как это работает:
- Мы устанавливаем переменную 'found' в ноль, оценивая false
- если найдено совпадение для 'TERMINATE' с регулярным выражением, мы устанавливаем его равным единице.
- Если наша переменная 'found' имеет значение True, выведите:)
Другие решения могут потреблять много памяти, если вы используете их для очень больших файлов.
Если я правильно понимаю ваш вопрос, вы хотите строки после TERMINATE
, не включая TERMINATE
-линия. awk
можно сделать это простым способом:
awk '{if(found) print} /TERMINATE/{found=1}' your_file
Объяснение:
- Хотя это не лучшая практика, вы можете полагаться на тот факт, что все переменные по умолчанию имеют значение 0 или пустую строку, если она не определена. Итак, первое выражение (
if(found) print
) не будет ничего печатать для начала. - После того, как печать завершена, мы проверяем, является ли это стартовой линией (которая не должна быть включена).
Это напечатает все строки после 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
$
Объяснение:
- Если найдена конечная строка, печать не производится. Обратите внимание, что эта проверка выполняется перед фактической печатью, чтобы исключить конечную строку из результата.
- Напечатать текущую строку, если
found
установлено. - Если начальная строка найдена, установите
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. Более того, если файл не содержит TERMINATE
1-я команда не выполняется.
Альтернативы отличным sed
ответ от jfgagne, и в котором нет соответствующей строки:
awk '/TERMINATE/ {y=1;next} y'
( /questions/2735507/grep-a-raspechatat-vse-posle-matcha/2735520#2735520)awk '/TERMINATE/ ? c++ : c'
( /questions/2735507/grep-a-raspechatat-vse-posle-matcha/2735514#2735514)perl -ne 'print unless 1 .. /TERMINATE/'
( /questions/2735507/grep-a-raspechatat-vse-posle-matcha/2735522#2735522)
В моей команде 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 и выражением вашего соответствия.