Có sự thay thế nào cho các công tắc -A -B -C của grep (để in vài dòng trước và sau) không?


10
grep -A 2 -B 3 

in 2 dòng sau chuỗi grep và in 3 dòng trước.

grep -C 3

in 3 dòng trước và 3 dòng sau

Thật không may, greptôi đang sử dụng không hỗ trợ các tùy chọn này. Có bất kỳ lệnh hoặc kịch bản thay thế có sẵn để mô phỏng điều này? Sử dụng sed/ awk/ perl/ shell script?


+1 Tôi không biết có một -Ccông tắc.
Lazer

1
Cài đặt grep của GNU. Tổng quát hơn, khi một máy Sun mới đến đây, bước đầu tiên trong quá trình thiết lập là thứ mà ai đó gọi GNU > /usr/local. Các chương trình GNU có rất nhiều phần mở rộng rất hữu ích và được thiết kế để tránh các hạn chế tùy ý (nhưng bạn phải trả giá đắt về kích thước và đôi khi hiệu suất). Nhiều hệ thống propietary có kho lưu trữ gói "không chính thức" với GNU và các công cụ khác. "Đối tác" sẽ không cho bạn biết về họ, ngay cả khi họ được quản lý bởi nhà cung cấp ...
vonbrand

Câu trả lời:


6

Một cách xấu vừa phải để làm điều đó là

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

hoặc thay thế -cbằng -C NUMcho NUMcác dòng của bối cảnh. Nó sẽ tạo ra đầu ra thêm, mặc dù. (Nếu diffhỗ trợ của bạn -u/ -U NUM, nó sẽ sạch hơn.)

Nếu bạn diffkhông có -c/ -C/ -u, vẫn có cách để làm điều đó, nhưng chúng khá xấu. Mặt khác, một hệ thống diffthậm chí không hỗ trợ cũng -ccó thể không có Perl.


Điều này thật tuyệt, Hoạt động như sự quyến rũ, mặc dù tôi đã phải sử dụng tùy chọn -bitw với điều này để làm cho nó hoạt động cho các tệp Windows được tạo.
Prashant Bhate

Bạn có thể gửi stdin tới diff và bỏ qua tạm thời:grep -v pattern file | diff -c - file
Cascabel

5

ack chỉ cần Perl, và bao gồm -A, -B-Ctùy chọn mà làm việc như grep của. Nó sử dụng cú pháp regex của Perl thay vì grep và cách nó chọn các tệp để tìm kiếm khá khác nhau. Bạn có thể muốn thử -ftùy chọn khi sử dụng nó (in ra các tệp mà nó sẽ tìm kiếm mà không thực sự tìm kiếm bất cứ thứ gì).

Nó có thể được cài đặt dưới dạng một tập lệnh không yêu cầu các mô-đun không cốt lõi. Chỉ cần thả nó vào ~/binthư mục của bạn (hoặc bất cứ nơi nào khác trên PATH của bạn mà bạn có quyền truy cập ghi) và đảm bảo rằng nó có thể chmodthực thi được.


Hộp sản xuất của nó và thật không may, tôi không có đủ đặc quyền để cài đặt bất cứ thứ gì, và tôi không thể mạo hiểm, tuy nhiên, nhờ mẹo này tôi sẽ cài đặt nó và thử trên máy tính xách tay gia đình của tôi
Prashant Bhate

@Prashant, bạn không cần root để cài đặt ackcho mục đích sử dụng của riêng bạn.
cjm

Có nhưng tôi vẫn không thể sử dụng nó ở đó, mặc dù chắc chắn rằng tập lệnh này sẽ tồn tại mãi mãi trong ~ / bin của tôi :)
Prashant Bhate

@Prashant: Tại sao bạn không thể sử dụng nó? Nó chỉ là một kịch bản perl.
trực giác

1
Hộp SẢN XUẤT của nó, cần phải có sự cho phép đặc biệt phê duyệt bla bla bla ... để làm bất cứ điều gì trên đó. và bất cứ điều gì đi sai có trên đến trên đầu tôi;) và nó không có giá trị nó :)
Prashant Bhate

5

Kịch bản perl đơn giản này mô phỏng grep -Aở một mức độ nào đó

#!/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
}

Lưu ý rằng bạn có thể thêm một tuyên bố sử dụng, để làm cho tập lệnh có thể đọc và sử dụng được;)

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

Nice, phiên bản perl nào tôi cần để chạy cái này?
Prashant Bhate

Tôi sử dụng v5.10.1, tôi đoán perl 5 là khá phổ biến những ngày này.
Vijay Anant

y.8.8.8 và nó hoạt động, thật tuyệt, nhưng tôi cần một kịch bản làm những gì -B làm
Prashant Bhate

Tốt Tôi sẽ chuyển thứ tự của các đối số, mặc dù; grep-A 3 footrông tự nhiên hơn nhiều grep-A foo 3. :-)
musiphil

3

Bạn chỉ có thể cài đặt GNU grep hoặc Ack (viết bằng Perl, hiểu nhiều tùy chọn GNU grep và nhiều hơn nữa).

Nếu bạn thích gắn bó với các công cụ tiêu chuẩn cộng với một chút kịch bản, thì đây là tập lệnh awk mô phỏng hành vi của các tùy chọn -A-Btùy chọn của GNU grep . Thử nghiệm tối thiểu.

#!/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

Chạy nó như grep-ac -vpattern=PATTERN -vbefore=NBEFORE -vafter=NAFTERnơi PATTERNlà mô hình để tìm kiếm (một biểu thức chính quy mở rộng với một vài bổ sung awk ), và NBEFORENAFTERlà những con số của dòng để in trước và sau khi một trận đấu tương ứng (mặc định là 0). Thí dụ:

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

Bất kỳ giải pháp nào lưu trữ dữ liệu trong mảng đều không có vấn đề ... như tôi đã đề cập trước đó, kích thước tệp là khá lớn và nó có thể bị quá tải. Ngoài ra awk trên hệ thống này không cho phép kích thước tệp hơn 3000 byte.
Prashant Bhate

2
@Prashant: Tôi không hiểu sự phản đối của bạn. Tập lệnh này xóa các dòng một khi chúng không đủ điều kiện để là dòng trước. Nó không sử dụng nhiều bộ nhớ hơn mức cần thiết theo yêu cầu, ngoại trừ việc awk có thể có chi phí hoạt động cao hơn chương trình mục đích đặc biệt (nhưng ít hơn Perl, mà bạn cũng đang xem xét). Tổng kích thước của tập tin là hoàn toàn không liên quan.
Gilles 'SO- ngừng trở nên xấu xa'

2
{ "exec" "awk" "-f" "$0" "$@"; }: cách rất tiện lợi để khắc phục những hạn chế trong phân tích cú pháp dòng shebang.
dubiousjim

2

Hóa ra việc mô phỏng -B khá khó khăn, vì các vấn đề nảy sinh khi bạn có các đường khớp nối trực tiếp với nhau. Điều này khá nhiều không cho phép sử dụng bất kỳ loại quét tập tin chuyển qua một lần.

Tôi nhận ra điều này trong khi chơi xung quanh với xấp xỉ sau:

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

Điều này sẽ hoạt động gần như chính xác như grep -A7 -B3, với cảnh báo được mô tả trong đoạn đầu tiên.

Một giải pháp thay thế (cũng là một tệp) cho vấn đề này là sử dụng perl để cung cấp cho sed chuỗi lệnh:

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

oneliner khá dài, nhưng, tập tin này rất lớn, vì vậy việc đẩy các dòng thành mảng trong trường hợp này là một ý tưởng tồi, phải không?
Prashant Bhate

Các shift @A if push(@A,$_)>7;bit chỉ giữ một mảng có kích thước tối đa 7 xung quanh. (đó là thông số -A của bạn). Tùy chọn thứ hai giữ một tệp cực kỳ nhỏ xung quanh (chỉ cần chạy perl mà không có lớp ngoài sed để xem những gì được tạo ra ở đó), nhưng nó đọc tệp hai lần.
user455

0

Sử dụng sedđầu tiên bạn có thể nhận được số dòng của dòng phù hợp, sụt & increment một số dòng được đưa ra trong một whilevòng lặp và sau đó sử dụng sed -n "n1,n2p"để in dòng leading ( n1) và dấu ( n2) ngữ cảnh (tương tự như sedthay thế được đề xuất bởi user455). Nhiều quá trình đọc có thể dẫn đến một hit hiệu suất mặc dù.

edcó thể trực tiếp tham chiếu các dòng trước và sau của một dòng phù hợp, nhưng không thành công nếu phạm vi dòng được chỉ định không tồn tại; ví dụ: dòng phù hợp là dòng số 2, nhưng phải in 5 dòng trước trận đấu. edDo đó, sử dụng nó là cần thiết để thêm một số dòng (trống) thích hợp ở đầu và cuối. (Đối với các tệp lớn edcó thể không phải là công cụ phù hợp, hãy xem: bfs - trình quét tệp lớn ).

# 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.