ค้นหาการเกิดขึ้นครั้งสุดท้ายของสตริงในหลายไฟล์


9

ฉันต้องการค้นหาไฟล์บันทึกหลายไฟล์ (ไฟล์ทั้งหมดที่สร้างใน 24 ชั่วโมงที่ผ่านมาทั้งหมดเก็บไว้ในไดเรกทอรีเดียวกัน) เพื่อค้นหาสตริงที่เกิดขึ้นครั้งล่าสุด นี่คือคำสั่งที่ฉันเขียน:

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

แต่นี่จะส่งคืนบรรทัดสุดท้ายสำหรับไฟล์เดียวเท่านั้น ข้อเสนอแนะเกี่ยวกับวิธีการปรับแต่งนี้เพื่อให้ได้ทุกบรรทัด?


คุณพยายามคว่ำหัวและ grep ตัวสุดท้ายหรือไม่? หา -mtime 1 | grep fileprefix | xargs tail -1 | grep 'search string'
Mathieu

คำตอบ:


4

สมมติว่าสิ่งอำนวยความสะดวกของ GNU:

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

คุณช่วยอธิบายวัตถุประสงค์ของ 'bash -c \' ได้อย่างละเอียดเพราะฉันใช้ bash shell อยู่แล้ว จุดประสงค์ของ '_ {} +' ด้วยในตอนท้าย
Lokesh

@Lokesh คุณจะได้รับการดำเนินการคำสั่งบนไฟล์โดยใช้find -execด้วยbash -cเรากำลังวางไข่bashเชลล์ที่วนรอบไฟล์ที่พบfindและดำเนินการtac .. | grep -m1 fileprefixในแต่ละ
iruvar

ฉันพยายามขยายการกรองสตริงสำหรับวงโดยรวมคำสั่งตัดเช่นสำหรับ f; ทำแทค "$ f" | grep -m1 fileprefix | cut -d '' -f4,7-8 แต่เดี๋ยวก่อนฉันใส่คำสั่ง cut มันทำให้ฉันเกิดข้อผิดพลาดในตอนท้ายของไฟล์ คุณช่วยแนะนำสิ่งที่ฉันทำผิดได้ไหม
Lokesh

@lokesh ใช้-d" "กับการตัด เครื่องหมายคำพูดคู่แทนเดี่ยว
iruvar

1
findคำสั่งสามารถกรองคำนำหน้าไฟล์; grepไม่ควรจำเป็นสำหรับการที่ นอกจากนี้ยังน่าแปลกใจที่สตริงการค้นหาไม่ได้คำตอบนี้
Jonathan Leffler

8

หากทุกอย่างอยู่ในไดเรกทอรีเดียวคุณสามารถทำได้:

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

หากไฟล์เหล่านี้มีขนาดใหญ่อาจเป็นการเพิ่มความเร็วtacให้กับการพิมพ์ไฟล์ในลำดับย้อนกลับ (บรรทัดสุดท้ายก่อน) จากนั้นจึงgrep -m1ตรงกับเหตุการณ์ที่เกิดขึ้นครั้งแรก ด้วยวิธีนี้คุณหลีกเลี่ยงการอ่านไฟล์ทั้งหมด:

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

fileprefixทั้งของผู้ที่ถือว่ามีไดเรกทอรีที่ตรงกับ หากมีคุณจะได้รับข้อผิดพลาดคุณสามารถเพิกเฉยได้ หากเป็นปัญหาให้ตรวจสอบเฉพาะไฟล์:

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

หากคุณต้องการพิมพ์ชื่อไฟล์ให้เพิ่ม-Hในแต่ละgrepการเรียกใช้ หรือถ้าคุณไม่สนับสนุนมันบอกว่ามันยังค้นหาผ่านgrep /dev/nullที่จะไม่เปลี่ยนผลลัพธ์ แต่เนื่องจากgrepได้รับหลายไฟล์มันจะพิมพ์ชื่อไฟล์สำหรับการเข้าชมแต่ละครั้ง:

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

“ ด้วยวิธีนี้คุณหลีกเลี่ยงการอ่านไฟล์ทั้งหมด” - เอ่อ? ไม่คุณหลีกเลี่ยงการอ่านไฟล์ทั้งหมดใน grep แต่คุณใส่ไฟล์ทั้งหมดผ่านแทคแทน ฉันยังไม่ชัดเจนว่ามันจะเร็วขึ้นแม้ว่ามันจะขึ้นอยู่กับว่าการแข่งขันใกล้เริ่มต้นหรือสิ้นสุดไฟล์
Gilles 'หยุดความชั่วร้าย'

@Gilles ไม่มีคุณไม่ใส่ไฟล์ทั้งหมดผ่านการtacอย่างใดอย่างหนึ่ง มันจะออกทันทีที่พบคู่แรก ฉันเพิ่งทดสอบด้วยไฟล์ข้อความ 832M และรูปแบบที่พบในบรรทัดสุดท้าย grep -m 1 pattern fileเครื่องมือ ~ 7 วินาทีและเอาtac file | grep -m1 pattern 0.009
terdon

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

... จะทำงานหากคุณมี GNU sedที่สนับสนุน-sไฟล์ eparate ตัวเลือกและ findPOSIX

อย่างไรก็ตามคุณควรเพิ่ม! -type dหรือตัว-type fระบุเนื่องจากการพยายามอ่านไดเรกทอรีจะไม่มีประโยชน์อย่างยิ่งและการ จำกัด ช่วงของไฟล์ปกติให้แคบลงสามารถหลีกเลี่ยงการหยุดการอ่านบนไพพ์หรือไฟล์อุปกรณ์อนุกรม

ตรรกะนั้นเรียบง่ายอย่างไม่น่าเชื่อ - sedเขียนทับhพื้นที่เก่าด้วยสำเนาของบรรทัดอินพุตใด ๆ ที่เข้าsearchstringกันแล้วdลบออกจากบรรทัดอินพุตทั้งหมด แต่สุดท้ายสำหรับไฟล์อินพุตแต่ละไฟล์ เมื่อมันมาถึงบรรทัดสุดท้ายมันจะxเปลี่ยนการเว้นวรรคการถือและรูปแบบของมันและดังนั้นหากsearchstringพบว่าในขณะที่มันอ่านไฟล์การเกิดขึ้นครั้งสุดท้ายดังกล่าวจะถูกพิมพ์โดยอัตโนมัติเพื่อเอาท์พุทมิฉะนั้นมันจะเขียนบรรทัดว่าง (เพิ่ม/./!dถึงหางของsedสคริปต์ถ้าเป็นที่ไม่พึงประสงค์)

วิธีนี้จะทำการsedเรียกใช้ไฟล์เดียวสำหรับไฟล์อินพุต 65k ไฟล์บางตัวหรือARG_MAXขีด จำกัดของคุณ นี่ควรเป็นวิธีแก้ปัญหาที่มีประสิทธิภาพมากและมีการใช้งานอย่างง่าย

หากคุณยังต้องการที่ชื่อไฟล์ที่ได้รับ GNU ล่าสุดsedคุณสามารถเขียนพวกเขาออกไปแยกบรรทัดที่มีFคำสั่งหรืออื่น ๆ ที่คุณสามารถรับได้พิมพ์โดยfindในรายการแยกต่างหากต่อชุดโดยการผนวกหลังจากหลัก-print+


1

เกี่ยวกับ:

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

ด้านบนให้ผลลัพธ์ที่ดีกับการเกิดขึ้นครั้งสุดท้ายของสตริงการค้นหาในแต่ละไฟล์ตามด้วยชื่อไฟล์ตามหลังเครื่องหมายจุลภาค (แก้ไขส่วน ", $ 1" ภายใต้เสียงก้องเพื่อเปลี่ยนการจัดรูปแบบหรือลบออกหากไม่จำเป็น) เอาต์พุตตัวอย่างที่ค้นหาสตริงการค้นหา '10' ในไฟล์ที่มีคำนำหน้าชื่อ "file" เป็นดังนี้:

[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] }}'

นี้ใช้ GNU grep's -Hและ-nตัวเลือกเสมอพิมพ์ทั้งชื่อไฟล์และ LINENUMBER ของการแข่งขันทั้งหมดแล้วมันจะเรียงลำดับโดยชื่อไฟล์และ LINENUMBER และท่อลงใน awk ซึ่งร้านค้านัดสุดท้ายสำหรับแต่ละชื่อไฟล์ในอาร์เรย์และในที่สุดก็พิมพ์ มัน.

วิธีการเดรัจฉานบังคับค่อนข้างใช้งานได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.