Есть ли альтернатива ключам -A -B -C grep (чтобы напечатать несколько строк до и после)?


10
grep -A 2 -B 3 

печатает 2 строки после строки grep и печатает 3 строки раньше.

grep -C 3

печатает 3 строки до и 3 строки после

К сожалению, grepя использую не поддерживает эти параметры. Существуют ли альтернативные команды или сценарии для имитации этого? Используете sed/ awk/ perl/ shell-скрипты?


+1 Я не знал, что там был -Cпереключатель.
Лазер

1
Установите GNU grep. В целом, когда сюда прибыла новая машина Sun, самым первым шагом в настройке было то, что кто-то назвал GNU > /usr/local. Программы GNU имеют множество очень полезных расширений и предназначены для избежания произвольных ограничений (но вы платите дорого за размер, а иногда и за производительность). Многие пропиетарные системы имеют «неофициальные» репозитории пакетов с GNU и другими инструментами. «Партнер» не скажет вам о них, даже когда ими управляет продавец ...
vonbrand

Ответы:


6

Один умеренно уродливый способ сделать это

grep -v pattern file >file.tmp; diff -c file.tmp file

или заменить -cна -C NUMдля NUMстрок контекста. Это произведет дополнительный выход, все же. (Если ваша diffподдержка -u/ -U NUM, это будет чище.)

Если у вас diffнет -c/ -C/ -u, есть еще способы сделать это, но они довольно уродливы. С другой стороны, система, diffкоторая даже не поддерживает, -cвероятно, также не имеет Perl.


Это круто, работает как шарм, хотя мне пришлось использовать параметр -bitw с этим, чтобы он работал для файлов, созданных Windows.
Прашант Бхате

Вы можете отправить стандартный ввод различий и пропустить временное:grep -v pattern file | diff -c - file
Каскабель

5

извед требуется только Perl, и включает в себя -A, -Bи-C варианты , что работа , как Grep - х. Он использует синтаксис регулярных выражений Perl вместо grep, и способ выбора файлов для поиска совершенно другой. Возможно, вы захотите попробовать эту -fопцию при ее использовании (которая распечатывает файлы, которые она будет искать, фактически ничего не ища).

Он может быть установлен как один скрипт, который не требует неосновных модулей. Просто поместите его в свой ~/binкаталог (или где-нибудь еще в вашем PATH, к которому у вас есть права на запись) и убедитесь, что он chmodисполняемый.


Его производственная коробка и, к сожалению, у меня недостаточно прав для установки чего-либо, и я не могу рисковать, хотя, спасибо за этот совет, я установлю его и попробую на своем домашнем ноутбуке
Prashant Bhate,

@Prashant, вам не нужен root для установки ackдля собственного использования.
CJM

Да, но я все еще не могу использовать его там, хотя уверен, что этот скрипт навсегда останется в моем ~ / bin :)
Prashant Bhate

@Prashant: Почему ты не можешь использовать это? Это всего лишь скрипт на Perl.
интуитивно

1
Его ПРОИЗВОДСТВЕННАЯ коробка, необходимо принять специальные разрешения разрешения бла бла бла ... сделать любую вещь на нем. и что-то идет не так, как надо, приходит мне в голову;) И это того не стоит :)
Prashant Bhate

5

Этот простой скрипт на Perl grep -Aдо некоторой степени эмулирует

#!/usr/bin/perl

$pattern=shift; #patthern to search
$lines=shift; # number of lines to print

$n = 0;
while (<>) {
  $n = $lines if /$pattern/; # reset counting
  if ($n) { print; $n-- } # print if within
  $n = 0 if eof; # don't leak across file boundaries
}

Обратите внимание, что вы можете добавить оператор использования, чтобы сделать сценарий читабельным и удобным для использования;)

USAGE:    $./grep-A.pl <pattern> <numLines> <filename> 

Хорошо, какая версия Perl мне нужна для этого?
Прашант Бхате

Я использую v5.10.1, я думаю, Perl 5 довольно распространен в наши дни.
Виджай Анант

у меня его 5.8.8, и он работает, отлично, но мне нужен скрипт, который делает то, что делает -B
Prashant Bhate

Хороший. Я бы поменял порядок аргументов, хотя; grep-A 3 fooвыглядит гораздо естественнее, чем grep-A foo 3. :-)
musiphil

3

Вы можете просто установить GNU grep или Ack (написано на Perl, понимает многие параметры GNU grep и многое другое).

Если вы предпочитаете использовать стандартные инструменты и немного сценариев, вот скрипт awk, который имитирует поведение GNU grep -Aи его -Bпараметров. Минимально проверено.

#!/bin/sh
# grep-ac: a grep-like awk script
# Arguments: pattern = awk regexp to search for
#            before = number of lines to print before a match
#            after = number of lines to print after a match
{ "exec" "awk" "-f" "$0" "$@"; }
# The array h contains the history of lines that haven't been printed
# but are eligible for being "before" lines.
# The variable until contains the number of the last "after" line to print.
match($0, pattern) {   # the current line matches
    for (i in h) {
        print h[i];    # print each remaining before line
        delete h[i];   # delete each line as it's printed
    }
    until=NR+after;    # record the last after line to print
}
{
    if (NR<=until) print $0;    # from a match to its last after line: print
    else h[NR]=$0;              # after that: save in history
    delete h[NR-before];        # remove line too old to be a before line
}
END {exit !until}               # exit status: 0 if there was a match, else 1

Запустите его, указав, grep-ac -vpattern=PATTERN -vbefore=NBEFORE -vafter=NAFTERгде PATTERNнаходится шаблон для поиска ( расширенное регулярное выражение с несколькими дополнениями в awk ), NBEFOREи NAFTERколичество строк, которые нужно напечатать до и после совпадения соответственно (по умолчанию 0). Пример:

<input_file grep-ac -vbefore=2 -vpattern='foo *bar'

Ни о каком решении, хранящем данные в массиве, не может быть и речи ... как я уже упоминал ранее, размер файла довольно велик и может привести к переполнению. Также awk в этой системе не допускает размер файла более 3000 байт.
Прашант Бхате

2
@Prashant: я не понимаю ваши возражения. Этот сценарий удаляет строки, когда они не могут быть перед строками. Он не использует больше памяти, чем это необходимо, учитывая требования, за исключением того, что awk может иметь более высокие издержки, чем специализированная программа (но меньше, чем Perl, который вы также рассматриваете). Общий размер файла совершенно не имеет значения.
Жиль "ТАК - перестань быть злым"

2
{ "exec" "awk" "-f" "$0" "$@"; }: очень изящный способ обойти ограничения в разборе строки shebang.
dubiousjim

2

Оказывается, довольно сложно эмулировать -B из-за проблем, которые возникают, когда у вас совпадают строки, следующие друг за другом напрямую. Это в значительной степени запрещает использование любого вида сквозного сканирования файлов.

Я понял это, играя со следующим приближением:

perl -pe 'if(/search_term/) {print foreach @A; print ">"; $B=4}; shift @A if push(@A, $_)>7; $_ = "" unless ($B-- > 0);' target_file

Это будет работать примерно так же, как и grep -A7 -B3, с оговоркой, описанной в первом абзаце.

Альтернативное (также однофайловое) решение этой проблемы заключается в использовании perl для подачи sed командной строки:

sed -n `perl -pe '$_=(/search_term/?sprintf("%d,%dp;", $.-3,$.+4):"")' file` file

довольно длинный, но этот файл очень большой, поэтому вставка строк в массив в этом случае - плохая идея, не так ли?
Прашант Бхате

shift @A if push(@A,$_)>7;Бит только сохраняет массив максимального размера 7 вокруг. (это ваш параметр -A). Второй вариант хранит невероятно маленький файл (просто запустите perl без внешнего слоя sed, чтобы увидеть, что там сгенерировано), но он читает файл дважды.
user455

0

Используя, sedвы можете сначала получить номера строк совпадающих строк, уменьшить и увеличить заданный номер строки в whileцикле, а затем использовать sed -n "n1,n2p"для печати строк контекста lead ( n1) и trailing ( n2) (аналогично sedальтернативе, предложенной пользователем 455). Однако многие процессы чтения могут привести к снижению производительности.

edможет напрямую ссылаться на предыдущую и последующую строки совпавшей строки, но завершается неудачно, если указанный диапазон строк не существует; например, совпадающая строка - это строка № 2, но должны быть напечатаны 5 строк перед совпадением. Используя edего , поэтому необходимо добавить соответствующее количество (пустых) строк в начале и в конце. (Однако, для больших файлов edможет не подойти , см .: bfs - сканер больших файлов ).

# sample code to match lines with number 5 plus previous & following line
# (using Bash)
printf '%s\n' {1..20} > num.txt

# sed
sed -n '/5/=' num.txt | while read num; do
   n1=$((num - 1))
   n2=$((num + 1))
   [[ $n1 -lt 1 ]] && n1=1
   sed -n "${n1},${n2}p" num.txt
   echo --
done | sed -e '${/^--$/d;}'

# ed
cat <<-'EOF' | ed -s num.txt | sed -e $'N;N;a\\\n--' | sed -e '${/^--$/d;}'
H
0i
beginning: added line one
.
$a
end: added line one
.
,g/5/km\
'm-1,'m+1p
q
EOF
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.