Giới hạn ngữ cảnh grep cho N ký tự trên dòng


31

Tôi phải grep qua một số tệp JSON trong đó độ dài dòng vượt quá vài nghìn ký tự. Làm cách nào tôi có thể giới hạn grep để hiển thị ngữ cảnh tối đa N ký tự ở bên trái và bên phải của trận đấu? Bất kỳ công cụ nào khác ngoài grep cũng sẽ ổn, miễn là nó có sẵn trong các gói Linux phổ biến.

Đây sẽ là đầu ra ví dụ, cho công tắc grep tưởng tượng Ф :

$ grep -r foo *
hello.txt: Once upon a time a big foo came out of the woods.

$ grep -Ф 10 -r foo *
hello.txt: ime a big foo came of t



3
Không trùng lặp. Đây là khoảng ± ký tự nhưng thay thế được đề xuất của bạn là khoảng ± dòng. ( Tuy nhiên, tham chiếu của bạn đến stackoverflow là tốt.)
roaima

Câu trả lời:


22

Với GNU grep:

N=10; grep -roP ".{0,$N}foo.{0,$N}" .

Giải trình:

  • -o => Chỉ in những gì bạn khớp
  • -P => Sử dụng biểu thức chính quy theo kiểu Perl
  • Regex cho biết khớp 0 với các $Nký tự theo foosau là 0 đến các $Nký tự.

Nếu bạn không có GNU grep:

find . -type f -exec \
    perl -nle '
        BEGIN{$N=10}
        print if s/^.*?(.{0,$N}foo.{0,$N}).*?$/$ARGV:$1/
    ' {} \;

Giải trình:

Vì chúng tôi không còn có thể dựa vào grepGNU grep, chúng tôi sử dụng findđể tìm kiếm các tệp đệ quy ( -rhành động của GNU grep). Đối với mỗi tệp được tìm thấy, chúng tôi thực thi đoạn mã Perl.

Công tắc Perl:

  • -n Đọc từng dòng tệp
  • -l Xóa dòng mới ở cuối mỗi dòng và đặt lại khi in
  • -e Xử lý chuỗi sau đây dưới dạng mã

Đoạn mã Perl đang làm về cơ bản giống như grep. Nó bắt đầu bằng cách đặt một biến $Ncho số lượng ký tự ngữ cảnh bạn muốn. Điều BEGIN{}này có nghĩa là điều này chỉ được thực hiện một lần khi bắt đầu thực hiện không phải một lần cho mỗi dòng trong mỗi tệp.

Câu lệnh được thực thi cho mỗi dòng là in dòng nếu thay thế regex hoạt động.

Các regex:

  • Khớp bất kỳ thứ cũ nào một cách lười biếng 1 ở đầu dòng ( ^.*?) theo sau .{0,$N}như trong greptrường hợp, tiếp footheo là một thứ khác .{0,$N}và cuối cùng khớp với bất kỳ thứ cũ nào một cách lười biếng cho đến cuối dòng ( .*?$).
  • Chúng tôi thay thế bằng $ARGV:$1. $ARGVlà một biến số ma thuật chứa tên của tệp hiện tại đang được đọc. $1là những gì parens phù hợp: bối cảnh trong trường hợp này.
  • Các trận đấu lười biếng ở hai đầu được yêu cầu vì một trận đấu tham lam sẽ ăn tất cả các nhân vật trước foomà không khớp (vì .{0,$N}được phép khớp 0 lần).

1 Nghĩa là, không nên khớp bất cứ thứ gì trừ khi điều này sẽ khiến trận đấu tổng thể thất bại. Trong ngắn hạn, phù hợp với càng ít nhân vật càng tốt.


Rất tuyệt cảm ơn bạn. Điều này có nhược điểm là làm nổi bật toàn bộ đầu ra, không chỉ tìm kiếm văn bản mà còn có thể được xử lý bằng cách nối thêm | grep foovào cuối (tuy nhiên mất phần tô sáng tên tệp trong quy trình).
dotancohen

1
@dotancohen Tôi đoán bạn không thể chiến thắng tất cả :)
Joseph R.

w / GNU grepbạn có thể chỉ định màu sắc phù hợp / ứng dụng dựa trên các cờ được áp dụng thông qua các biến môi trường. vì vậy thậm chí có thể bạn có thể giành được tất cả, (không hứa hẹn - thậm chí không chắc nó sẽ hoạt động trong trường hợp này) nhưng cá nhân tôi không thấy sự liên quan ở đây ... dù sao thì ... hãy tiếp tục chơi.
mikeerv

Câu trả lời tốt đẹp. Chỉ cần một lưu ý, sử dụng zshtôi không thể làm cho nó hoạt động vượt qua N = 10 như trong ví dụ. Tuy nhiên nó không hoạt động nếu tôi export N=10trước khi chạy lệnh. Bất kỳ ý tưởng làm thế nào để điều chỉnh ví dụ để làm việc với zsh?
Gabe Kopley

Hoặcperl -lne 'print "$ARGV: $_" for /.{0,10}foo.{0,10}/g'
Stéphane Chazelas

19

Hãy thử sử dụng cái này:

grep -r -E -o ".{0,10}wantedText.{0,10}" *

-E nói rằng bạn muốn sử dụng regex mở rộng

-o nói, rằng bạn chỉ muốn in trận đấu

-r grep đang tìm kiếm kết quả đệ quy trong thư mục

ĐĂNG KÝ:

{0,10} cho biết, bạn muốn in bao nhiêu ký tự tùy ý

. đại diện cho một nhân vật tùy ý (bản thân một nhân vật không quan trọng ở đây, chỉ là số của họ)

Chỉnh sửa: Ồ, tôi hiểu rồi, Joseph đề xuất gần như cùng một giải pháp như tôi: D


Cảm ơn bạn. Mặc dù về cơ bản là cùng một giải pháp, nhưng thật tự tin rằng đây là phương pháp tốt nhất khi hai người độc lập giới thiệu nó.
dotancohen

Bạn được chào đón, cộng đồng Unix chỉ đơn giản là phải hợp tác, đó là những gì chúng ta đang có :-)
Eenoku

2
Mặc dù chúng giống nhau nhưng câu trả lời được chấp nhận không phù hợp với tôi (vẫn tạo ra các dòng dài), nhưng câu trả lời này đã làm được. Thủ thuật với N = 10 không hoạt động với vỏ bash.
meesern

trong cygwin -E là nhanh hơn đáng kể so với -P.
Bob Stein

2

Lấy từ: http://www.toporms.net/blog/2016/08/18/truncate-long-matching-lines-of-grep-a-solution-that-preserves-color/https: // stackoverflow. com / a / 39029954/1150462

Cách tiếp cận ".{0,10}<original pattern>.{0,10}"được đề xuất là hoàn toàn tốt ngoại trừ màu sắc nổi bật thường bị rối tung. Tôi đã tạo một tập lệnh có đầu ra tương tự nhưng màu sắc cũng được giữ nguyên:

#!/bin/bash

# Usage:
#   grepl PATTERN [FILE]

# how many characters around the searching keyword should be shown?
context_length=10

# What is the length of the control character for the color before and after the matching string?
# This is mostly determined by the environmental variable GREP_COLORS.
control_length_before=$(($(echo a | grep --color=always a | cut -d a -f '1' | wc -c)-1))
control_length_after=$(($(echo a | grep --color=always a | cut -d a -f '2' | wc -c)-1))

grep -E --color=always "$1" $2 | grep --color=none -oE ".{0,$(($control_length_before + $context_length))}$1.{0,$(($control_length_after + $context_length))}"

Giả sử tập lệnh được lưu dưới dạng grepl, sau đó grepl pattern file_with_long_linessẽ hiển thị các dòng khớp nhưng chỉ có 10 ký tự xung quanh chuỗi khớp.


0

Đường ống dẫn đến cutvới -bcờ; bạn có thể hướng dẫn đầu ra của grep chỉ các byte từ 1 đến 400 trên mỗi dòng.

grep "foobar" * | cut -b 1-400
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.