Bagaimana saya bisa mengecat isi file yang ditemukan menggunakan find menjadi satu file?

11

Saya berhasil menembak diri sendiri di tempat yang menyakitkan (sangat buruk) dengan memformat ulang sebuah partisi yang menyimpan data berharga. Tentu saja itu tidak disengaja, tetapi itu terjadi.

Namun, saya berhasil menggunakan testdiskdan photorecmemulihkan sebagian besar data. Jadi sekarang saya memiliki semua data yang didistribusikan lebih dari hampir 25.000 direktori. Sebagian besar file adalah file .txt, sedangkan sisanya adalah file gambar. Ada lebih dari 300 file .txt di setiap direktori.

Saya bisa grepatau gunakan finduntuk mengekstraksi string tertentu dari file .txt dan mengeluarkannya ke file. Misalnya, inilah baris yang saya gunakan untuk memverifikasi bahwa data saya ada di file yang dipulihkan:

find ./recup*/ -name '*.txt' -print | xargs grep -i "searchPattern"

Saya dapat menampilkan "searchPattern" ke file, tetapi itu hanya memberi saya pola itu. Inilah yang ingin saya capai:

Telusuri semua file dan cari string tertentu. Jika string itu ditemukan dalam file, cat SEMUA konten file itu ke file output. Jika pola ditemukan di lebih dari satu file, tambahkan konten file berikutnya ke file output. Perhatikan bahwa saya tidak ingin menampilkan pola yang saya cari, tetapi SEMUA isi file di mana pola ditemukan.

Saya pikir ini bisa dilakukan, tetapi saya tidak tahu bagaimana cara mengambil semua isi file setelah mengambil pola tertentu darinya.

Ami
sumber
Jadi dengan perintah yang Anda berikan, itu memberi Anda hasil yang Anda cari tetapi Anda ingin mengarahkan output ke file teks?
ryekayo
Setelah membaca pertanyaan saya, paragraf yang dimulai dengan "Melewati ..." itu terdengar seperti psuedocode. Mungkin saya bisa mendapatkannya kode dengan beberapa baris untuk / jika kode Python. Akan mencobanya sementara aku menunggu respons yang lebih terinformasi
Ami
Ini tentu saja kode psued, dan saya yakin Anda dapat menemukan cara untuk melakukannya di bash juga.
ryekayo
@ryekayo, Ya, itu memberi saya output, tapi itu hanya untuk menemukan file apa tipe data tertentu, yang memberitahu saya bahwa lebih banyak data yang ada di file itu. Jadi saya ingin mengambil semua yang ada di file itu dan menulisnya ke file lain.
Ami
Anda mungkin dapat membungkus perintah itu dalam semacam pernyataan if atau bahkan case-switch yang dapat memanggil fungsi yang dapat
mengecualikan

Jawaban:

10

Jika saya memahami tujuan Anda dengan benar, yang berikut ini akan melakukan apa yang Anda inginkan:

find ./recup*/ -name '*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Ini akan mencari semua *.txtfile di ./recup*/, menguji masing-masing searchPattern, jika cocok itu akan catfile. Output dari semua catfile ed akan diarahkan ke outputfile.txt.

Ulangi untuk setiap pola dan file keluaran.


Jika Anda memiliki banyak direktori yang cocok ./recup*, Anda mungkin berakhir dengan a argument list too long error. Cara sederhana untuk melakukannya adalah melakukan sesuatu seperti ini sebagai gantinya:

find ./ -mindepth 2 -path './recup*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Ini akan cocok dengan path lengkap. Jadi ./recup01234/foo/bar.txtakan dicocokkan. Itu -mindepth 2agar tidak cocok ./recup.txt, atau ./recup0.txt.

Patrick
sumber
Ya, saya pikir itu akan melakukannya. Dan itu memberi saya basis untuk bekerja. Karena saya akan mencari beberapa string, saya pikir sedikit kode / jika, dengan banyak elif akan membantu saya mengotomatiskan tugas. Terima kasih
Ami
Itu bahkan lebih baik daripada apa yang saya pikirkan lol
ryekayo
Tampaknya itu tidak berhasil. Mendapat kesalahan ini: "tidak dapat menjalankan / usr / bin / find: Daftar argumen terlalu panjang"
Ami
@Mi memperbarui jawaban untuk memberikan solusi untuk masalah itu.
Patrick
2
@ Ami Jika Anda menggunakan banyak string, mungkin lebih mudah untuk hanya menyimpan semua nama file positif ke file lain ( grep -l), lalu |sort|uniqdan catdari daftar file.
Sparhawk
3

Daripada mengeluarkan pola Anda, output nama file menggunakan "-l" pada grep, dan kemudian gunakan itu sebagai input ke cat.

find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern" | xargs cat

atau

cat $( find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern")

Saya menduga Anda dapat mengisi rincian lainnya. BTW, jika Anda mungkin memiliki spasi atau karakter aneh lainnya dalam nama file (tidak mungkin dalam kasus khusus ini, tetapi untuk tujuan di masa mendatang), gunakan -print0 pada find dan -Z pada grep, dikombinasikan dengan opsi -0 pada xargs untuk digunakan null byte antara nama file dan bukan baris baru.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat
dannysauer
sumber
2
Saya juga suka opsi "dua -exec" dari Patrick, kecuali bahwa itu akan menyebabkan garpu baru (well, clone ()) dan exec untuk setiap file. Biasanya Anda bisa menggunakan \+daripada \;menghindari masalah itu, tapi saya tidak tahu cara kerjanya dengan sepasang argumen -exec (saya curiga "buruk"). Menggunakan sepasang xargs, Anda hanya akan memiliki beberapa proses baru yang muncul, yang seharusnya lebih cepat dengan banyak file.
dannysauer
Ini terlihat bagus juga. Terima kasih. Satu pertanyaan noob: Kucing setelah xargs terakhir harus mengeluarkan ke file, kan?
Ami
Ketika saya pertama kali membacanya, saya tidak berpikir pertanyaan menentukan ke mana isi file harus pergi. Ketiga perintah ini meletakkan konten file pada STDOUT, jadi Anda hanya perlu menambahkan (sampai akhir) >afileatau |acommandatau apa pun yang sesuai untuk situasi Anda. :)
dannysauer
Jawaban yang bagus, saya perlu cat pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
App Work
Ini sedikit di luar topik, tapi saya lebih suka menggunakan sudo xargsdaripada xargs sudo. Ketika Anda menjalankan xargs sudo, itu membangun baris perintah dengan asumsi perintah itu sudo cat args. Tapi kucing di / bin, jadi sudo berjalan /bin/cat args. Jika perintah Anda berada di direktori yang lebih panjang, seperti / usr / local / bin, maka perintah sudo yang benar-benar berjalan dapat mengakibatkan baris perintah terlalu panjang dan kesalahan yang sulit dilacak. Selain itu, sudo xargshanya log yang Anda jalankan xargs, sementara xargs sudolog perintah dengan semua argumen - menghasilkan beberapa baris sudo log yang panjang. :)
dannysauer
1

Ini bukan kode yang optimal, tetapi sangat mudah dan akan bekerja dengan baik jika efisiensi tidak menjadi masalah. Masalahnya adalah bahwa ia akan menerobos file berkali-kali, bahkan jika string sudah ditemukan di dalamnya.

Pertama, cari string Anda dan tulis file yang cocok ke daftar.

find ./recup*/ -name '*.txt' -execdir grep -il "searchPattern" {} >> /tmp/file_list \;

Ulangi langkah ini menggantikan yang searchPatterndiperlukan. Ini menghasilkan daftar file yang cocok di /tmp/file_list.

Masalahnya adalah bahwa file ini mungkin memiliki duplikat di dalamnya. Karenanya, kita dapat mengganti duplikat dengan |sort|uniq. Bagian ini sortmenempatkan duplikat yang berdekatan satu sama lain, sehingga uniqdapat menghapusnya. Kemudian Anda dapat catmenggunakan file-file ini bersama-sama xargs(dengan setiap nama file dipisahkan oleh baris baru \n). Karenanya,

</tmp/file_list sort | uniq | xargs -d "\n" cat > final_file.txt

Tidak seperti jawaban lain, ini memiliki dua langkah di dalamnya, dan file sementara, jadi saya benar-benar hanya merekomendasikannya jika Anda memiliki beberapa pola untuk ditemukan.

Sparhawk
sumber
0

Tergantung pada shell dan lingkungan Anda, Anda dapat melakukannya seperti ini (dalam bash)

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1\|searchPattern2\|searchPattern3' "$file"; then
    cat "$file" >> some/other/file
  fi
done < <(find ./recup*/ -name '*.txt' -print0)

Jika Anda ingin memisahkan hasil berdasarkan pola, Anda dapat memodifikasinya menjadi seperti

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1' "$file"; then
    cat "$file" >> some/other/file1
  elif grep -qim1 'searchPattern2' "$file"; then
    cat "$file" >> some/other/file2
  elif grep -qim1 'searchPattern3' "$file"; then
    cat "$file" >> some/other/file3
  fi
done < <(find ./recup*/ -name '*.txt' -print0)
Steeldriver
sumber
Apa yang dilakukan bit setelah "selesai"? Apa yang sebenarnya saya suka adalah memodifikasi jika blok sehingga file yang berisi pola yang cocok ditulis ke yang berbeda.
Ami
Itu hanya daftar file '.txt' yang ditemukan, masing-masing diakhiri oleh karakter nol (sehingga aman untuk nama file yang mengandung spasi dan karakter lain). The whileLoop kemudian membaca bahwa daftar dan melakukan grep/ kondisional catbagian.
steeldriver
Ketika saya mencoba menjalankan kode, saya mendapatkan kesalahan ini: ./recoverData.sh: Kesalahan sintaks: "(" tidak terduga. Itu berasal dari tanda kurung di sekitar perintah find
Ami
Shell apa yang Anda gunakan? sintaks substitusi proses khusus untuk bash - maka kualifikasi saya "Tergantung pada shell dan lingkungan Anda"
steeldriver
1
Anda bisa mengeksekusi perintah langsung di bash shell interaktif, atau meletakkannya di file yang baris pertamanya berisi shebang #!/bin/bash, membuatnya bisa dieksekusi chmod +x recoverData.sh, dan mengeksekusinya menggunakan ./recoverData.sh. Jangan tidak menggunakan sh recoverData.shkarena /bin/shkemungkinan sebuah dashshell .
steeldriver