Temukan kemunculan string terakhir di banyak file

9

Saya perlu mencari beberapa file log (semua file yang dihasilkan dalam 24 jam terakhir, semua disimpan di direktori yang sama) untuk menemukan kemunculan terakhir string. Ini adalah perintah yang saya tulis:

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

Tetapi ini hanya mengembalikan baris terakhir untuk satu file. Ada saran tentang cara men-tweak ini untuk mendapatkan semua baris?

Lokesh
sumber
Apakah Anda mencoba membalikkan ekor dan grep terakhir? Temukan . -minggu 1 | grep fileprefix | xargs tail -1 | grep 'search string'
Mathieu

Jawaban:

4

Dengan asumsi fasilitas GNU:

find . -mtime -1 -exec bash -c \
'for f; do tac "$f" | grep -m1 fileprefix; done' _ {} +
iruvar
sumber
Bisakah Anda menjelaskan tujuan 'bash -c \' karena saya sudah menggunakan bash shell. Juga tujuan '_ {} +' di akhir.
Lokesh
@Lokesh, Anda bisa findmenjalankan perintah pada file menggunakan -exec. Dengan bash -c, kami menelurkan bashshell yang loop melalui file yang ditemukan oleh finddan mengeksekusi tac .. | grep -m1 fileprefixpada masing
iruvar
Saya mencoba untuk memperluas penyaringan string untuk loop dengan memasukkan perintah cut yaitu untuk f; do tac "$ f" | grep -m1 fileprefix | cut -d '' -f4,7-8 tetapi saat saya menaruh perintah cut itu memberi saya kesalahan akhir file yang tidak terduga. Bisakah Anda menyarankan kesalahan saya?
Lokesh
@lokesh, gunakan -d" "dengan potongan. Kutipan ganda alih-alih tunggal
iruvar
1
The findperintah dapat menyaring untuk awalan berkas; yang grepseharusnya tidak diperlukan untuk itu. Mengejutkan juga bahwa string pencarian tidak menemukan jawaban ini.
Jonathan Leffler
8

Jika semuanya ada dalam satu direktori, Anda dapat melakukan:

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

Jika ini adalah file besar, mungkin perlu mempercepat dengan menggunakan tacuntuk mencetak file dalam urutan terbalik (baris terakhir terlebih dahulu) dan kemudian grep -m1untuk mencocokkan kejadian pertama. Dengan begitu, Anda menghindari harus membaca seluruh file:

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

Keduanya menganggap tidak ada direktori yang cocok fileprefix. Jika ada, Anda akan mendapatkan kesalahan yang bisa Anda abaikan. Jika itu masalah, periksa hanya file:

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

Jika Anda juga perlu nama file dicetak, tambahkan -Hke setiap grepdoa. Atau, jika Anda greptidak mendukungnya, katakan juga untuk mencari /dev/null. Itu tidak akan mengubah output tetapi karena grepdiberikan beberapa file, itu akan selalu mencetak nama file untuk setiap klik:

for file in *fileprefix*; do
    grep 'search string' "$file" /dev/null | tail -1
done
terdon
sumber
"Dengan begitu, kamu menghindari harus membaca seluruh file" - uh? Tidak, Anda menghindari membaca seluruh file dalam grep tetapi Anda meletakkan seluruh file melalui tac sebagai gantinya. Tidak jelas bagi saya bahwa ini akan lebih cepat, meskipun itu akan tergantung pada apakah pertandingan sudah dekat awal atau akhir file.
Gilles 'SANGAT berhenti menjadi jahat'
@Gilles tidak, Anda juga tidak perlu memasukkan seluruh file tac. Itu akan keluar segera setelah pertandingan pertama ditemukan. Saya baru saja menguji dengan file teks 832M dan pola yang ditemukan pada baris terakhir. grep -m 1 pattern filealat ~ 7 detik dan tac file | grep -m1 patternbutuh 0.009.
terdon
4
find . ! -name . -prune -mtime 1 -name 'fileprefix*' \
     -exec sed -se'/searchstring/h;$!d;x' {} +

... akan berfungsi jika Anda memiliki GNU sedyang mendukung -sopsi file eparate dan POSIX find.

Anda mungkin harus menambahkan ! -type datau -type fkualifikasi, karena mencoba membaca direktori tidak akan sangat berguna, dan semakin mempersempit rentang ke file biasa dapat menghindari pembacaan tergantung pada pipa atau file perangkat serial.

Logikanya sangat sederhana - sedmenimpa hruang lamanya dengan salinan dari setiap jalur input yang cocok searchstring, kemudian dmenghapus dari output semua jalur input tetapi yang terakhir untuk setiap file input. Ketika sampai ke baris terakhir, ia xmengubah ruang penahanan dan pola, dan jika searchstringditemukan sama sekali saat membaca file, kejadian terakhir seperti itu akan dicetak ulang secara otomatis ke output, jika tidak maka akan menulis baris kosong. (tambahkan /./!dke ekor sedskrip jika itu tidak diinginkan) .

Ini akan melakukan seddoa tunggal per 65k beberapa file input - atau berapa pun ARG_MAXbatas Anda . Ini harus menjadi solusi yang sangat performan, dan cukup sederhana diimplementasikan.

Jika Anda juga menginginkan nama file, diberikan GNU baru-baru ini sedAnda dapat menuliskannya ke baris yang terpisah dengan Fperintah, atau Anda dapat membuatnya dicetak oleh finddalam daftar terpisah per batch dengan menambahkan -printprimer setelahnya +.

mikeserv
sumber
1

Bagaimana tentang:

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

Di atas memberikan Anda hasil yang bagus dengan kemunculan terakhir dari string pencarian di setiap file diikuti oleh nama file masing-masing setelah koma (memodifikasi bagian ", $ 1" di bawah gema untuk mengubah format atau menghapusnya jika tidak perlu). Contoh output yang mencari string pencarian '10' dalam file dengan awalan nama "file" adalah sebagai berikut:

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

Ini menggunakan GNU grep's -Hdan -npilihan untuk selalu mencetak kedua nama file dan linenumber dari semua pertandingan, maka macam oleh nama file dan linenumber, dan pipa ke awk, yang menyimpan pertandingan terakhir untuk setiap nama file dalam array, dan akhirnya mencetak Itu.

Metode yang cukup kasar, tetapi berhasil.

cas
sumber