Cari beberapa string dalam satu baris menggunakan regex di file/direktori bersarang dan keluaran hasil yang cocok

Misalnya jika ada file dan direktori:

/tmp/temp_dir/subdir_001/file_001.txt
/tmp/temp_dir/subdir_001/file_002.txt
/tmp/temp_dir/subdir_002/file_003.txt
/tmp/temp_dir/subdir_003/file_004.txt

Dan itu memiliki berbagai konten dengan baris tertentu yang dapat ditemukan dengan regex. Misalnya berikut isi file file_001.txt:

abc cba
little boy writes -54321_12345 and goes to street 987
bca acb
little boy writes -12345_54321 and jumps to street 789
cab bac

Yang saya minati adalah baris-baris yang dimulai dengan little boy writes. Saya menggunakan pola regex ini untuk menemukan data penting yang ingin saya simpan sebagai output: little boy writes (\-\d+\_\d+).*street (\d+)

Bagaimana saya bisa mencarinya secara rekursif dan hanya menghasilkan string yang cocok? Jadi dalam file keluaran saya hanya akan memiliki ini:

54321_12345 987
12345_54321 789

person iorsa    schedule 30.07.2020    source sumber
comment
Hal tentang menggunakan find dan exec.   -  person Raman Sailopal    schedule 30.07.2020


Jawaban (3)


Kombinasi find dan sed akan berhasil :

find /tmp/temp_dir/ -type f -exec sed -En 's/little boy writes -([0-9]+_[0-9]+).*street ([0-9]+)/\1 \2/p' {} + > output

Perincian :

  • find /tmp/temp_dir/ -type f : kami menemukan setiap file secara rekursif dari folder root
  • -exec sed '... ' {} + menjalankan perintah pada setiap file yang ditemukan (di sini {} mewakili item yang diambil oleh perintah find, dan + berarti perintah dijalankan sekali terhadap hasil akhir, seperti yang dijelaskan di sini)
  • sed -En 's/little boy writes -([0-9]+_[0-9]+).*street ([0-9]+)/\1 \2/p' : kami menjalankan pola yang Anda jelaskan dalam pertanyaan Anda, menggunakan sed (\d bukan kelas karakter sed yang valid, kami menggunakan [0-9] sebagai gantinya)
  • > output kami mengarahkan output dari perintah ini ke file bernama output
person Aserre    schedule 30.07.2020

Anda dapat menggunakan grep dikombinasikan dengan sed:

$ grep '^little boy writes' /tmp/temp_dir/subdir_*/file_*.txt | sed -re 's/^.* -([0-9]+_[0-9]+).*street ([0-9]+)/\1 \2/' > output.txt
person Joe    schedule 30.07.2020

Anda bisa mendapatkan baris hanya dengan grep rekursif, dengan atau tanpa nama file.

grep -r  '^little boy writes' *  # lists source filenames
grep -hr '^little boy writes' *  # does not

Namun, ini melaporkan keseluruhan baris. Pencocokan pola Perl (-P) dengan -o mungkin dapat mendeteksi garis yang benar dan hanya mengembalikan bit yang Anda inginkan, tetapi polanya akan sangat buruk untuk dipahami dan dipelihara oleh kebanyakan orang, jadi mungkin perlu proses kedua -

grep -hr '^little boy writes' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt |
  sed -E 's/[^0-9_]*([0-9_]+)/\1 /g'

atau jika Anda benar-benar ingin menghindari ruang tersebut pada akhirnya,

grep -hr '^little boy writes' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt |
  's/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/\1 \2/'

Tetapi jika Anda tahu persis di mana file-file itu cukup baik untuk melakukan globbing seperti itu, yang Anda perlukan hanyalah sed.

sed -En '/^little boy writes/{ s/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/\1 \2/g; p; }' /tmp/temp_dir/subdir_[0-9][0-9][0-9]/file_[0-9][0-9][0-9].txt

Jika tidak, grep dan/atau sed mungkin menghabiskan banyak data yang sebenarnya bisa Anda hindari...dan mungkin struktur direktori Anda tidak begitu konsisten. Dalam hal ini, shopt akan membantu.

shopt -s globstar # let's ** stand for variable depth of subdirectories
sed -En '/^little boy writes/{ s/^[^0-9_]*([0-9_]+)[^0-9_]*([0-9_]+$)/\1 \2/g; p; }' **/file_[0-9][0-9][0-9].txt

Ini seharusnya jauh lebih efisien (dan lebih cepat). Ini akan membiarkan OS memilih file yang cocok dan hanya menyerahkan file tersebut ke sed untuk dipindai.

Ini juga hanya menggunakan satu contoh sed, daripada memunculkan satu contoh untuk setiap file dengan find atau memerlukan xargs.

Semoga beruntung.

person Paul Hodges    schedule 30.07.2020