Tìm sự xuất hiện cuối cùng của chuỗi trong nhiều tệp


9

Tôi cần tìm kiếm nhiều tệp nhật ký (tất cả các tệp được tạo trong 24 giờ qua, tất cả được giữ trong cùng một thư mục) để tìm sự xuất hiện cuối cùng của chuỗi. Đây là lệnh tôi đã viết:

find . -mtime 1 | grep fileprefix | xargs grep 'search string' | tail -1

Nhưng điều này chỉ trả về dòng cuối cùng cho một tệp. Bất kỳ đề xuất về làm thế nào để điều chỉnh này để có được tất cả các dòng?


Bạn đã thử đảo ngược đuôi và grep cuối cùng? tìm thấy . -Thời gian 1 | grep fileprefix | xargs đuôi -1 | grep 'chuỗi tìm kiếm'
Mathieu

Câu trả lời:


4

Giả sử các cơ sở GNU:

find . -mtime -1 -exec bash -c \
'for f; do tac "$f" | grep -m1 fileprefix; done' _ {} +

Bạn có thể vui lòng giải thích mục đích của 'bash -c \' vì tôi đã sử dụng bash shell. Ngoài ra mục đích của '_ {} +' ở cuối.
Lokesh

@Lokesh, bạn có thể findthực hiện các lệnh trên tệp bằng cách sử dụng -exec. Với bash -c, chúng tôi sẽ tạo ra một bashlớp vỏ vòng qua các tệp được tìm thấy findvà thực thi tac .. | grep -m1 fileprefixtrên mỗi
tệp

Tôi đã cố gắng mở rộng bộ lọc chuỗi trong vòng lặp for bằng cách bao gồm lệnh cut tức là cho f; làm "$ f" | grep -m1 fileprefix | cut -d '' -f4,7-8 nhưng khoảnh khắc tôi đặt lệnh cắt nó sẽ báo lỗi kết thúc tập tin không mong muốn. Bạn có thể vui lòng đề nghị những gì tôi đang làm sai.
Lokesh

@lokesh, dùng -d" "cắt. Dấu ngoặc kép thay vì đơn
iruvar

1
Các findlệnh có thể lọc cho tiền tố tập tin; các grepkhông nên cần thiết cho điều đó. Điều đáng ngạc nhiên là chuỗi tìm kiếm không có trong câu trả lời này.
Jonathan Leffler

8

Nếu mọi thứ đều nằm trong một thư mục, bạn có thể làm:

for file in *fileprefix*; do
    grep 'search string' "$file" | tail -1
done

Nếu đây là các tệp lớn, có thể đáng để tăng tốc mọi thứ bằng cách sử dụng tacđể in tệp theo thứ tự ngược lại (dòng cuối cùng trước) và sau đó grep -m1để khớp với lần xuất hiện đầu tiên. Bằng cách đó, bạn tránh phải đọc toàn bộ tệp:

for file in *fileprefix*; do
    tac file | grep -m1 'search string'
done

Cả hai đều cho rằng không có thư mục phù hợp fileprefix. Nếu có, bạn sẽ gặp một lỗi mà bạn có thể bỏ qua. Nếu đó là một vấn đề, chỉ kiểm tra các tệp:

 for file in *fileprefix*; do
    [ -f "$file" ] && tac file | grep -m1 'search string'
 done

Nếu bạn cũng cần tên tệp được in, thêm -Hvào mỗi greplời gọi. Hoặc, nếu bạn grepkhông hỗ trợ nó, hãy bảo nó cũng tìm kiếm thông qua /dev/null. Điều đó sẽ không thay đổi đầu ra nhưng vì grepđược cung cấp nhiều tệp, nó sẽ luôn in tên tệp cho mỗi lần nhấn:

for file in *fileprefix*; do
    grep 'search string' "$file" /dev/null | tail -1
done

Bằng cách đó, bạn tránh phải đọc toàn bộ tập tin. Không, bạn tránh đọc toàn bộ tệp trong grep nhưng thay vào đó bạn đặt toàn bộ tệp qua tac. Tôi không rõ ràng rằng điều này sẽ nhanh hơn, mặc dù nó sẽ phụ thuộc vào việc trận đấu đã ở gần đầu hay cuối tập tin.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles không, bạn cũng không đặt toàn bộ tập tin tac. Nó sẽ thoát ngay khi trận đấu đầu tiên được tìm thấy. Tôi vừa thử nghiệm với tệp văn bản 832M và một mẫu được tìm thấy ở dòng cuối cùng. grep -m 1 pattern filecông cụ ~ 7 giây và tac file | grep -m1 patternmất 0.009.
terdon

4
find . ! -name . -prune -mtime 1 -name 'fileprefix*' \
     -exec sed -se'/searchstring/h;$!d;x' {} +

... sẽ hoạt động nếu bạn có GNU sedhỗ trợ -stùy chọn tệp eparate và POSIX find.

Tuy nhiên, bạn có thể nên thêm ! -type dhoặc các -type fvòng loại, vì cố gắng đọc một thư mục sẽ không hữu ích, và việc thu hẹp phạm vi vào các tệp thông thường có thể tránh việc đọc bị treo trên một đường ống hoặc tệp thiết bị nối tiếp.

Logic rất đơn giản - sedghi đè lên hkhông gian cũ của nó bằng một bản sao của bất kỳ dòng đầu vào nào khớp searchstring, sau đó dxóa khỏi đầu ra tất cả các dòng đầu vào nhưng cuối cùng cho mỗi tệp đầu vào. Khi đến dòng cuối cùng, nó sẽ xthay đổi không gian giữ và mẫu của nó, và vì vậy nếu searchstringđược tìm thấy trong khi nó đọc tệp thì lần xuất hiện cuối cùng như vậy sẽ được tự động in thành đầu ra, nếu không nó sẽ viết một dòng trống. (thêm /./!dvào phần đuôi của sedtập lệnh nếu điều đó là không mong muốn) .

Điều này sẽ thực hiện một lệnh sedgọi duy nhất cho mỗi tệp 65k đầu vào - hoặc bất kể ARG_MAXgiới hạn của bạn là gì. Đây phải là một giải pháp rất hiệu quả và được thực hiện khá đơn giản.

Nếu bạn cũng muốn tên tệp, được cung cấp một GNU gần đây, sedbạn có thể viết chúng ra các dòng riêng biệt bằng Flệnh hoặc nếu không bạn có thể in chúng findtrong một danh sách riêng cho mỗi lô bằng cách nối thêm -printchính sau +.


1

Làm thế nào về:

find . -mtime -1 -name "fileprefix*" -exec sh -c \
'echo "$(grep 'search string' $1 | tail -n 1),$1"' _ {} \;

Ở trên cung cấp cho bạn một đầu ra đẹp với lần xuất hiện cuối cùng của chuỗi tìm kiếm trong mỗi tệp theo sau là tên tệp tương ứng sau dấu phẩy (sửa đổi phần ", $ 1" dưới tiếng vang để thay đổi định dạng hoặc xóa nó nếu không cần thiết). Đầu ra mẫu tìm kiếm chuỗi tìm kiếm '10' trong các tệp có tiền tố tên "tệp" như sau:

[dmitry@localhost sourceDir]$ find . -mtime -1 -name "file*" -exec  sh -c 'echo "$(grep '10' $1 | tail -n 1),$1"' _ {} \;
Another data 02 10,./file02.log
Some data 01 10,./file01.log
Yet another data 03 10,./file03.log 

1
find . -mtime 1 -name 'fileprefix*' -exec grep -Hn 'search string' {} + |
    sort -t: -k1,2 -n | 
    awk -F: '{key=$1 ; $1="" ; $2="" ; gsub(/^  /,"",$0); a[key]=$0} 
             END {for (key in a) { print key ":" a[key] }}'

Điều này sử dụng grepcác tùy chọn -H-ntùy chọn của GNU để luôn in cả tên tệp và số vải lanh của tất cả các kết quả khớp, sau đó sắp xếp theo tên tệp và vải lanh và đặt nó vào awk, lưu trữ kết quả khớp cuối cùng cho mỗi tên tệp trong một mảng và cuối cùng in nó

Một phương pháp khá vũ phu, nhưng nó hoạt động.

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.