Secara rekursif mencari file dengan ekstensi tertentu

437

Saya mencoba untuk menemukan semua file dengan ekstensi spesifik dalam direktori dan subdirektori dengan bash saya (Rilis LTS Ubuntu Terbaru).

Inilah yang ditulis dalam file skrip:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*; 
  do
    echo "dir :$directory"
    echo "filename: $i"
    #   echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then        

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then  
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

Sayangnya, ketika saya memulai skrip ini di terminal, ia mengatakan:

[: 29: in: unexpected operator

(dengan $extensionalih - alih 'in')

Apa yang terjadi di sini, di mana kesalahannya? Tapi kurung kurawal ini

Balik
sumber
2
Kesalahan berasal dari '{'
shrewmouse

Jawaban:

750
find $directory -type f -name "*.in"

sedikit lebih pendek dari itu semua (dan lebih aman - berkaitan dengan spasi putih dalam nama file dan nama direktori).

Skrip Anda mungkin gagal untuk entri yang tidak memiliki .nama mereka, membuatnya $extensionkosong.

Tikar
sumber
16
ya, findbersifat rekursif secara default. Anda dapat membatasi kedalaman jika Anda mau (lihat halaman manual).
Mat
1
Saya ingin meneruskan semua file yang ditemukan sebagai argumen ke file jar. Bagaimana ini bisa dilakukan?
balik
8
@ flip: itu pertanyaan yang berbeda. Posting pertanyaan baru, merinci dengan tepat apa yang ingin Anda lakukan dan apa yang telah Anda coba sejauh ini.
Mat
Satu koreksi kecil: gunakan '* .in' atau \ *. Sebagai ganti "* .in" karena tanda kutip ganda tidak mencegah ekspansi shell. Yaitu skrip Anda tidak akan berfungsi dengan baik jika ada file dengan ekstensi .in di direktori saat ini.
Shnatsel
4
@ Shnatsel: tanda kutip ganda mencegah ekspansi shell. Cobalah.
Mat
188
find {directory} -type f -name '*.extension'

Contoh: Untuk menemukan semua csvfile di direktori saat ini dan sub-direktori, gunakan:

find . -type f -name '*.csv'
Mohammad AlQanneh
sumber
60

Sintaks yang saya gunakan sedikit berbeda dari yang disarankan @Matt:

find $directory -type f -name \*.in

(kurang satu penekanan tombol).

Scott C Wilson
sumber
1
Skrip Matt juga tidak akan berfungsi jika ada file dengan ekstensi .in di direktori saat ini, sementara milik Anda masih berfungsi. Lihat stackoverflow.com/questions/5927369/…
Shnatsel
4
@ Shnatsel komentar ini (dan karenanya milik Anda) jelas salah.
gniourf_gniourf
1
@ gniourf_gniourf Anda harus memberikan beberapa referensi untuk pernyataan Anda, jika tidak orang bisa saja berdebat: "Tidak, Anda salah". Tetapi kenyataannya Anda benar: gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel
@ user1885518: Saya pikir seharusnya orang yang mengklaim bahwa skrip tidak berfungsi yang harus memberikan beberapa contoh di mana skrip gagal. Itulah yang saya lakukan ketika saya meninggalkan komentar di mana ada skrip yang rusak: biasanya tentang kutipan dan nama file yang mengandung spasi, baris baru, gumpalan, dll., Dan saya secara khusus menjelaskan mengapa itu rusak.
gniourf_gniourf
2
Memberikan referensi selalu merupakan cara yang baik dalam diskusi, tidak tergantung pada siapa yang pertama. Dia seharusnya, kamu harus.
Murmel
14

Tanpa menggunakan find:

du -a $directory | awk '{print $2}' | grep '\.in$'
rtrn
sumber
3
Tidak grepperlu di sini. awkmemiliki ekspresi reguler dan dapat membatasi output ke nilai yang cocok dengan suatu pola.
Kenster
Metode ini sangat berguna jika Anda melewati 100-an terabyte. Perintah find membutuhkan terlalu banyak waktu untuk diproses. Ini segera dimulai.
Protonova
1
awk|grepadalah anti-pola. Biarkan awk melakukan grepping.
Jens
10
  1. Ada yang {hilang setelah itubrowsefolders ()
  2. Semua $inharus$suffix
  3. Garis dengan cutmembuat Anda hanya bagian tengah front.middle.extension. Anda harus membaca manual shell Anda ${varname%%pattern}dan teman-teman.

Saya menganggap Anda melakukan ini sebagai latihan dalam skrip shell, sebaliknya find solusi yang sudah diusulkan adalah cara untuk pergi.

Untuk memeriksa sintaks shell yang tepat, tanpa menjalankan skrip, gunakan sh -n scriptname.

Jens
sumber
10
find "$PWD" -type f -name "*.in"
kip2
sumber
7

Meskipun menggunakan findperintah dapat berguna di sini, shell itu sendiri menyediakan opsi untuk mencapai persyaratan ini tanpa alat pihak ketiga. Itubash shell menyediakan pilihan dukungan gumpal diperpanjang menggunakan yang Anda dapat mendapatkan nama file di bawah jalur rekursif bahwa pertandingan dengan ekstensi yang Anda inginkan.

Opsi yang diperluas adalah extglobyang perlu diatur menggunakan shoptopsi seperti di bawah ini. Opsi diaktifkan dengan -sdukungan dan dinonaktifkan dengan -ubendera. Selain itu Anda dapat menggunakan beberapa opsi lebih banyak yaitu nullglobdi mana gumpalan yang tak tertandingi dihanyutkan seluruhnya, diganti dengan serangkaian kata-kata nol. Dan globstaritu memungkinkan untuk berulang melalui semua direktori

shopt -s extglob nullglob globstar

Sekarang yang perlu Anda lakukan adalah membentuk ekspresi glob untuk menyertakan file-file dari ekstensi tertentu yang dapat Anda lakukan seperti di bawah ini. Kami menggunakan array untuk mengisi hasil glob karena ketika dikutip dengan benar dan diperluas, nama file dengan karakter khusus akan tetap utuh dan tidak rusak karena pemisahan kata oleh shell.

Misalnya untuk mendaftar semua *.csvfile di jalur rekursif

fileList=(**/*.csv)

Pilihannya **adalah berulang melalui sub-folder dan *.csvmerupakan ekspansi global untuk memasukkan file ekstensi yang disebutkan. Sekarang untuk mencetak file yang sebenarnya, lakukan saja

printf '%s\n' "${fileList[@]}"

Menggunakan array dan melakukan ekspansi kutipan yang tepat adalah cara yang tepat ketika digunakan dalam skrip shell, tetapi untuk penggunaan interaktif, Anda bisa menggunakan lsdengan ekspresi glob sebagai

ls -1 -- **/*.csv

Ini bisa sangat diperluas untuk mencocokkan beberapa file yaitu file yang diakhiri dengan beberapa ekstensi (yaitu mirip dengan menambahkan beberapa flag dalam findperintah). Sebagai contoh, pertimbangkan kasus yang membutuhkan untuk mendapatkan semua file gambar rekursif yaitu ekstensi *.gif, *.pngdan *.jpg, yang Anda butuhkan adalah

ls -1 -- **/+(*.jpg|*.gif|*.png)

Ini bisa sangat diperluas untuk memiliki hasil negasi juga. Dengan sintaksis yang sama, seseorang dapat menggunakan hasil dari glob untuk mengecualikan file dari tipe tertentu. Anggap Anda ingin mengecualikan nama file dengan ekstensi di atas, Anda bisa melakukannya

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

Konstruksinya !()adalah operasi meniadakan untuk tidak menyertakan ekstensi file yang tercantum di dalam dan |merupakan operator bergantian seperti yang digunakan di pustaka Ekspresi Reguler Diperpanjang untuk melakukan pencocokan ATAU dari gumpalan.

Perhatikan bahwa dukungan glob yang diperluas ini tidak tersedia di shell bourne POSIX dan murni khusus untuk versi terbaru dari bash. Jadi jika Anda mempertimbangkan portabilitas skrip yang berjalan di POSIX dan bashshell, opsi ini tidak akan tepat.

Inian
sumber
6

Untuk menemukan semua pom.xmlfile di direktori Anda saat ini dan mencetaknya, Anda dapat menggunakan:

find . -name 'pom.xml' -print
Bharat Yadav
sumber
1
find $directory -type f -name "*.in"|grep $substring
Sergiu
sumber
0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 
Avinash Kumar Mishra
sumber
1
Sementara kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang mengapa dan / atau bagaimana kode ini menjawab pertanyaan meningkatkan nilai jangka panjangnya.
rollstuhlfahrer