Чи є альтернатива grep's -A -B -C перемикачам (для друку декількох рядків до і після)?


10
grep -A 2 -B 3 

друкує 2 рядки після рядка grep і друкує 3 рядки раніше.

grep -C 3

друкує 3 рядки до та 3 рядки після

На жаль, grepці параметри я не використовую. Чи є альтернативні команди чи скрипт для їх моделювання? Використання sed/ awk/ perlскрипти / оболонки?


+1 Я не знав, що є -Cперемикач.
Лазер

1
Встановіть греп GNU. Загалом, коли сюди приїхала нова машина 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.
Прашант Бхат

Ви можете надіслати stdin до diff та пропустити тимчасові:grep -v pattern file | diff -c - file
Cascabel

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 досить поширений в наші дні.
Vijay Anant

ya її 5.8.8, і це працює, чудово, але мені потрібен сценарій, який робить те, що робити -B
Prashant Bhate

Добре. Я б перейшов на зміну порядку аргументів; grep-A 3 fooвиглядає набагато природніше, ніж grep-A foo 3. :-)
musiphil

3

Ви можете просто встановити GNU grep або Ack (написаний Perl, розуміє багато варіантів grep GNU та інше).

Якщо ви віддаєте перевагу дотримуватися стандартних інструментів плюс трохи сценаріїв, ось сценарій awk, який імітує поведінку грепів GNU -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є шаблон пошуку ( розширений регулярний вираз з кількома додатковими доповненнями ), NBEFOREі NAFTERє число рядків для друку відповідно до і після відповідності відповідно (за замовчуванням до 0). Приклад:

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

Будь-яке рішення, яке зберігає дані в масиві, не викликає сумнівів ... як я вже згадував, розмір файлів досить величезний, і він може перетікати. Також awk у цій системі не дозволяє розміром файлу більше 3000 байт.
Прашант Бхат

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

2
{ "exec" "awk" "-f" "$0" "$@"; }: дуже вишуканий спосіб подолати обмеження при синтаксичному синтаксичному розборі.
сумнівним

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

досить довгий oneliner, але цей файл дуже величезний, тому просування рядків у масив у цьому випадку погана ідея, чи не так?
Прашант Бхат

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

0

Використовуючи sedви можете спочатку отримати номери рядків відповідних ліній, зменшення та збільшення заданого номера рядка в whileциклі, а потім використовувати sed -n "n1,n2p"для друку рядків провідного ( n1) та трейлінг ( 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.