Menghapus file dengan ekstensi tertentu kecuali satu file dari terminal

36

Saya perlu menghapus semua file dengan ekstensi .gif kecuali satu file dengan nama say "filename.gif". Apa cara optimal untuk melakukan ini di terminal?

Perintah rm *.gifmenghapus semua file gif termasuk file filename.gif.

Seth
sumber

Jawaban:

66

Inilah solusi sederhana yang mungkin sebaiknya Anda gunakan:

mv filename.gif filename.gif.keep
rm *.gif
mv filename.gif.keep filename.gif

Tidak ada yang istimewa dengan .keepekstensi, ini hanya membuatnya agar nama file sementara tidak berakhir .gif.

Jika Anda tidak boleh mengganti nama file (dan ada situasi skrip di mana ini penting):

for X in *.gif; do
    if [ "$X" != "filename.gif" ]; then
        rm "$X"
    fi
done

Atau Anda dapat menulisnya lebih pendek seperti ini:

for X in *.gif; do [ "$X" != "filename.gif" ] && rm "$X"; done

Anda mungkin lebih suka menggunakan find; itu sangat kuat, Anda mungkin menganggapnya lebih mudah dibaca, dan lebih baik menangani nama file aneh dengan karakter seperti *di dalamnya .

find . -maxdepth 1 -not -name 'filename.gif' -name '*.gif' -delete

Saya telah menggunakan -notoperator untuk keterbacaan, tetapi jika kepatuhan POSIX penting - jika Anda tidak menggunakan GNU find, atau jika ini untuk skrip Anda bermaksud mendistribusikan ulang ke orang lain atau berjalan di berbagai sistem - Anda harus gunakan !operator sebagai gantinya:

find . -maxdepth 1 ! -name 'filename.gif' -name '*.gif' -delete

Satu hal praktis tentang hal findini adalah Anda dapat dengan mudah memodifikasi perintah untuk case-insensitivity, sehingga ia menemukan dan menghapus file dengan ekstensi seperti .GIFjuga:

find . -maxdepth 1 -not -name 'filename.gif' -iname '*.gif' -delete

Harap dicatat bahwa saya telah digunakan -inamedi tempat -nameuntuk pola pencarian *.giftapi saya tidak menggunakannya untuk filename.gif. Agaknya Anda tahu persis apa nama file Anda, dan -inameakan cocok dengan huruf kapital alternatif tidak hanya dalam ekstensi , tetapi di mana saja dalam nama file.

Semua solusi ini hanya menghapus file yang berada langsung di direktori saat ini . Mereka tidak menghapus file yang tidak ada dalam direktori saat ini, dan mereka tidak menghapus file yang berada di subdirektori dari direktori saat ini.

Jika Anda ingin menghapus file di mana saja yang terdapat dalam direktori saat ini (yaitu, termasuk dalam subdirektori, dan dalam subdirektori dari subdirektori tersebut, dan sebagainya - file yang terkandung dalam direktori saat ini atau salah satu turunannya), gunakan find tanpa maxdepth -1 :

find . -not -name 'filename.gif' -name '*.gif' -delete

Hati-hati dengan ini!

Anda juga dapat mengatur nilai lain dengan -maxdepth. Misalnya, untuk menghapus file di direktori saat ini dan anak-cucunya tetapi tidak lebih dalam:

find . -maxdepth 3 -not -name 'filename.gif' -name '*.gif' -delete

Pastikan Anda tidak pernah memprioritaskan -delete, sebelum ekspresi lainnya! Anda akan melihat saya selalu menempatkan -deletedi akhir. Jika Anda meletakkannya di awal, itu akan dievaluasi terlebih dahulu, dan semua file di bawah .(termasuk file yang tidak berakhir .gifdan file dalam sub-sub-sub ... direktori dari .) akan dihapus!

Untuk informasi lebih lanjut , lihat halaman manual untuk bashdan shdan untuk perintah yang digunakan dalam contoh ini: mv, rm, [, dan (terutama) find.

Eliah Kagan
sumber
1
Terima kasih Eliah. Saya melakukannya mirip dengan metode pertama yang Anda sarankan yaitu untuk mengkonversi file dalam format yang berbeda dan mengubahnya kembali. Tetapi itu tampak agak kurang optimal dan tidak bersih. Saya suka pendekatan kedua Anda dan @efthialex telah menyarankan dan saya menggunakannya sekarang. Terima kasih!
1
@Marvis Saya senang ini memenuhi kebutuhan Anda. Omong-omong, saya telah memperluas jawaban ini dengan informasi tentang metode lain (atau kelas metode) yang digunakan find, yang sangat fleksibel.
Eliah Kagan
3
Terima kasih atas jawaban terinci findyang memang merupakan temuan yang bagus.
1
+1 untuk jawaban akal sehat (pindah-hapus-pindah).
tu-Reinstate Monica-dor duh
Nit-pick: bagaimana findmenangani nama file aneh dengan *mereka lebih baik daripada for-loop? Untuk loop menangani file dengan *baik, karena Anda telah menggunakan tanda kutip ganda.
Flimm
28

Jika Anda menggunakan bash(shell default), extglobopsi shell memungkinkan Anda untuk menggunakan sintaks yang cocok dengan pola yang diperluas. Untuk mengaktifkannya, gunakan shoptperintah builtin:

shopt -s extglob

(Saya menyertakan baris itu di .bashrcfile saya .)

Antara lain, itu memberikan akses ke !()operator, yang cocok dengan pola apa pun yang tidak di dalam parens. Untuk tujuan Anda:

rm !(filename).gif

Informasi lebih lanjut tersedia di man bashbawah "Pencocokan Pola".

Eswald
sumber
1
Jawaban yang sangat bagus! Ini khusus untuk bash jadi mungkin tidak cocok untuk skrip portabel, tetapi tugas interaktif dan bahkan beberapa skrip, ini adalah cara terbaik untuk melakukannya. Ini membawa kompleksitas tambahan yang extglobharus diaktifkan, tetapi tidak ada hal buruk terjadi jika dinonaktifkan (dalam hal ini, ini tidak berfungsi). Dan tampaknya diaktifkan secara default, meskipun saya tidak memiliki shopt -s extglobfile konfigurasi (atau global) saya. Jika ada penjelasan sederhana mengapa itu berhasil bagi saya di luar kotak, Anda mungkin ingin menambahkannya ke jawaban ini; atau saya dapat memposting pertanyaan baru.
Eliah Kagan
13

Buka Terminal dengan Ctrl+ Alt+ Tdan ketik:

find . -type f -name "*.gif" -and -not -name "filename.gif" -exec rm -vf {} \;

Perbedaan antara -deletedan -exec rm -vf {} \;adalah bahwa dengan opsi kedua, Anda akan dapat melihat file mana yang telah dihapus, karena -vbenderanya. Itu sesuatu yang tidak bisa dilakukan dengan -deleteopsi. ( -name -deleteakan mencetak nama file, tetapi rmdengan -vmengungkapkan jika setiap file berhasil dihapus.)

efthialex
sumber
1

Ada GLOBIGNOREvariabelnya. Dari man bash:

GLOBIGNORE
      A colon-separated list of patterns defining the set of filenames
      to be ignored by pathname expansion.  If a filename matched by a
      pathname expansion pattern also matches one of the  patterns  in
      GLOBIGNORE, it is removed from the list of matches.

Jadi, cara sederhana adalah dengan hanya mengatur GLOBGINOREnama file yang dimaksud:

$ touch {a,b,c,d}.png
$ echo *.png
a.png b.png c.png d.png
$ GLOBIGNORE=c.png
$ echo *.png
a.png b.png d.png

Jadi, dalam kasus Anda:

GLOBIGNORE=filename.gif; rm *.gif

Tentu saja, karena GLOBIGNOREberisi pola, Anda dapat menggunakannya kapan pun Anda ingin mengecualikan pola:

$ GLOBIGNORE='[a-c].png'; echo *.png
d.png
$ touch bd.png; GLOBIGNORE='?.png'; echo *.png
bd.png

Keuntungan (?!) Dari GLOBIGNOREpengaturan itu secara otomatis mengecualikan .dan.. tidak cocok, dan memungkinkan dotglob:

The  GLOBIGNORE shell variable may be used to restrict the set of file‐
names matching a pattern.  If GLOBIGNORE is set, each matching filename
that also matches one of the patterns in GLOBIGNORE is removed from the
list of matches.  The filenames ``.''  and ``..''  are  always  ignored
when  GLOBIGNORE is set and not null.  However, setting GLOBIGNORE to a
non-null value has the effect of enabling the dotglob shell option,  so
all other filenames beginning with a ``.''  will match.  To get the old
behavior of ignoring filenames beginning with a ``.'', make ``.*''  one
of  the  patterns  in  GLOBIGNORE.  The dotglob option is disabled when
GLOBIGNORE is unset.

Jadi, cara sederhana menghapus semua file dan folder dalam direktori:

GLOBIGNORE=.; rm -r *

The *akan cocok nama file yang dimulai dengan ., sejak GLOBIGNOREdiaktifkan dotglob, tetapi tidak akan cocok .atau .., sehingga Anda tidak akan mempengaruhi direktori induk seperti itu.

muru
sumber