Ulangi semua file dengan ekstensi tertentu

109
for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Saya ingin mengulang setiap file di folder saat ini dan memeriksa apakah cocok dengan ekstensi tertentu. Kode diatas tidak berfungsi, tahukah anda kenapa?

AR89
sumber
4
Tentang apa for i in $(ls *.java); do echo "do something with file $i"; done?
speakr
tidak ada cara untuk memperbaikinya jika pernyataan?
AR89
1
Anda membandingkan $idengan string literal "* .java"; perluasan pola tidak dilakukan di sini.
chepner
Untuk memperbaiki pernyataan if seperti yang Anda miliki, gunakan if [[ $i == *.java ]]; then.. (perhatikan tanda ganda [[]] s dan * .java tanpa tanda kutip).
orang lain itu
2
Jangan parsels - terima jawaban @ chepner
glenn jackman

Jawaban:

201

Tidak ada trik mewah yang dibutuhkan:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

Penjaga memastikan bahwa jika tidak ada file yang cocok, loop akan keluar tanpa mencoba memproses nama file yang tidak ada *.java. Dalam bash(atau shell yang mendukung sesuatu yang serupa), Anda dapat menggunakan nullglobopsi untuk mengabaikan kecocokan yang gagal dan tidak memasuki isi loop.

shopt -s nullglob
for i in *.java; do
    ...
done
chepner
sumber
5
Cara paling mudah adalah dengan menambahkan pola lain: for i in *.java *.cpp; do. Jika Anda telah mengaktifkan pola tambahan bashdengan shopt -s extglob, Anda dapat menulis for i in *.@(java|cpp); do.
chepner
8
Ini akan terjadi jika itu benar-benar cocok dengan file apa pun. Anda perlu menggunakan shopt -s nullglobagar pola yang tidak cocok meluas ke urutan kosong daripada diperlakukan secara harfiah.
chepner
1
@zygimantus ya seharusnya, selama direktori saat ini adalah tempat skrip dijalankan. Jika Anda tidak berada di direktori yang Anda inginkan, Anda harus cdke direktori itu sebelum memulai forperulangan
danielsdesk
1
@puk Itu tergantung pada opsi shell Anda. Secara default, pola yang tidak cocok diperlakukan sebagai string literal. Anda dapat mengatur nullglobagar diperlakukan sebagai urutan kosong, atau mengaturnya failglobagar diperlakukan sebagai kesalahan. (Jika Anda menyetel keduanya, failglobdiutamakan.)
chepner
3
@chepner Mungkin berguna bagi non ahli untuk menunjukkan ini dalam jawaban karena seseorang dapat menyalin dan menempelkannya dan merasa tidak berfungsi. Contoh sempurna adalah seseorang yang mengonversi semua file ".jpg" menjadi " .png" sebelum menjalankan fungsi penting
puk
13

jawaban yang benar adalah @ chepner's

EXT=java
for i in *.${EXT}; do
    ...
done

Namun, berikut adalah trik kecil untuk memeriksa apakah nama file memiliki ekstensi tertentu:

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done
umläute
sumber
bagaimana jadinya dengan variabel, extbukan .java?
AR89
13

Tambahkan subfolder secara berulang,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done
luismartingil.dll
sumber
alih-alih find . -name "*.java" -type f -exec echo \{\} \; untuk menghindari kesalahan penguraian pada outputfind
umläute
1
Dan jika tidak, setidaknya Anda harus mengutip "$i"di dalam lingkaran.
tripleee
2
Ini tidak akan berfungsi jika ada file yang memiliki spasi dalam namanya.
codeforester
1
@codeforester Saya memiliki masalah itu dan saya telah memperbaikinya menggunakan metode dari jawaban ini: askubuntu.com/a/343753/551184
fsinisi90
7

Loop melalui semua file yang berakhir dengan: .img, .bin, .txtakhiran, dan mencetak nama file:

for i in *.img *.bin *.txt;
do
  echo "$i"
done

Atau secara rekursif (temukan juga di semua subdirektori):

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done
Benny
sumber
3

Saya setuju dengan jawaban lain mengenai cara yang benar untuk mengulang melalui file. Namun OP bertanya:

Kode diatas tidak berfungsi, tahukah anda kenapa?

Iya!

Artikel yang sangat baik Apa perbedaan antara test, [dan [[?] Menjelaskan secara rinci bahwa di antara perbedaan lainnya, Anda tidak dapat menggunakan expression matchingatau pattern matchingdalam testperintah (yang merupakan singkatan dari [)

Menampilkan pengujian baru [[pengujian lama [Contoh

Pencocokan pola = (atau ==) (tidak tersedia) [[$ name = a *]] || echo "nama tidak dimulai dengan 'a': $ name"

Ekspresi Reguler = ~ (tidak tersedia) [[$ (tanggal) = ~ ^ Jum \ ... \ 13]] && echo "Ini hari Jumat tanggal 13!"
sesuai

Jadi inilah alasan skrip Anda gagal. Jika OP tertarik dengan jawaban dengan [[sintaks (yang memiliki kelemahan karena tidak didukung pada banyak platform seperti [perintah), saya akan dengan senang hati mengedit jawaban saya untuk memasukkannya.

EDIT: Protips apa pun tentang cara memformat data dalam jawaban sebagai tabel akan sangat membantu!

pengguna000001
sumber
2

seperti yang dikatakan @chepner dalam komentarnya Anda membandingkan $ i dengan string tetap.

Untuk memperluas dan memperbaiki situasi, Anda harus menggunakan [[]] dengan operator regex = ~

misalnya:

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

regex di sebelah kanan = ~ diuji terhadap nilai operator sebelah kiri dan tidak boleh dikutip, (dikutip tidak akan salah tetapi akan dibandingkan dengan string tetap dan kemungkinan besar akan gagal "

tetapi jawaban @chepner di atas menggunakan glob adalah mekanisme yang jauh lebih efisien.

petech
sumber
bagaimana jadinya dengan variabel, extbukan .java?
AR89
1
ack, tidak perlu ekspresi reguler: if [[ $i == *.java ]]atau if [[ $i == *.$ext ]]. Tapi jangan parsing ls
glenn jackman
1

Saya menemukan solusi ini cukup berguna. Ini menggunakan -oropsi di find:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

Ini akan menemukan file dengan ekstensi tex, pngdan pdf.

f0nzie.dll
sumber