Hapus semua file kecuali file dengan ekstensi pdf dalam direktori

50

Saya memiliki direktori yang berisi yang berikut:

x.pdf
y.zip
z.mp3
a.pdf

Saya ingin menghapus semua file selain x.pdfdan a.pdf. Bagaimana saya melakukan ini dari terminal? Tidak ada subdirektori sehingga tidak perlu rekursi.

Starkers
sumber

Jawaban:

63
cd <the directory you want>
find . -type f ! -iname "*.pdf" -delete
  • Perintah pertama akan membawa Anda ke direktori di mana Anda ingin menghapus file Anda
  • Perintah kedua akan menghapus semua file kecuali yang berakhiran dengan .pdfnama file

Misalnya, jika ada direktori yang dipanggil tempdi folder rumah Anda:

cd ~/temp

lalu hapus file:

find . -type f ! -iname "*.pdf" -delete

Ini akan menghapus semua file kecuali xyz.pdf.

Anda dapat menggabungkan dua perintah ini untuk:

find ~/temp -type f ! -iname "*.pdf" -delete

.adalah direktori saat ini. !berarti mengambil semua file kecuali yang ada .pdfdi akhir. -type fhanya memilih file, bukan direktori. -deleteberarti menghapusnya.

CATATAN: perintah ini akan menghapus semua file (kecuali file pdf tetapi termasuk file tersembunyi) dalam direktori saat ini serta di semua sub-direktori. !harus datang sebelumnya -name. hanya -nameakan mencakup saja .pdf, sementara -inameakan mencakup keduanya .pdfdan.PDF

Untuk menghapus hanya di direktori saat ini dan tidak di sub-direktori tambahkan -maxdepth 1:

find . -maxdepth 1 -type f ! -iname "*.pdf" -delete
Edward Torvalds
sumber
Terima kasih atas jawabannya. Bisakah Anda membantu saya memahami sedikit sintaks? .berarti "dan"? !berarti "kecuali" -namemenandakan bahwa Anda ingin mengecualikan dengan parameter nama dan kemudian -deleteadalah tindakan yang harus diambil setelah menemukan? Jadi mencari semuanya kecuali "* .pdf" dan menghapusnya? Atau apakah saya salah paham?
jessenorton
.berarti direktori saat ini. !artinya mengambil semua file kecuali yang ada .pdfdi akhir. -deleteberarti menghapusnya. apakah saya jelas sekarang?
Edward Torvalds
@terdon starkers mengatakan bahwa tidak ada sub-directories.wait sakit mengedit jawaban saya untuk menjadi lebih luas
Edward Torvalds
+1 Anda harus menyertakan -maxdepth 1parameter untuk memulai. Kemudian sarankan untuk menghapus parameter jika seseorang ingin menghapus secara rekursif.
Tulains Córdova
3
ini dibawa ke perhatian saya bahwa kita harus menggunakan -inamebukan -name, atau file dengan .PDFsebagai perpanjangan akan lolos.
muru
43

Dengan bashperpanjangan shell, Anda bisa menghapus file apa pun dengan ekstensi selain .pdfmenggunakan

rm -- *.!(pdf)

Sebagaimana dicatat oleh @pts, --karakter menunjukkan akhir dari opsi perintah apa pun, membuat perintah aman dalam kasus yang jarang terjadi dari file yang namanya dimulai dengan -karakter.

Jika Anda ingin menghapus file tanpa ekstensi apa pun serta ekstensi selain .pdf, maka seperti yang ditunjukkan oleh @DennisWilliamson, Anda dapat menggunakan

rm -- !(*.pdf)

Perpanjangan globbing harus diaktifkan secara default, tetapi jika tidak Anda dapat melakukannya dengan menggunakan

shopt -s extglob

Terutama jika Anda berniat untuk menggunakan ini di dalam skrip, penting untuk dicatat bahwa jika ekspresi tidak cocok dengan apa pun (yaitu jika tidak ada file non-pdf dalam direktori), maka secara default glob akan diteruskan tidak diperluas ke rmperintah, menghasilkan kesalahan seperti

rm: cannot remove `*.!(pdf)': No such file or directory

Anda dapat mengubah perilaku default ini menggunakan nullglobopsi shell, namun itu memiliki masalah sendiri. Untuk diskusi yang lebih menyeluruh lihat NullGlob - Greg's Wiki

Steeldriver
sumber
Pendekatan IMO yang lebih baik.
Takkat
Bagaimana dengan file tanpa ekstensi? FWIW, di zsh iturm *~*.pdf
Emil Jeřábek
1
Saya akan meletakkan titik di dalam tanda kurung.
Dennis Williamson
4
Ah, tanda bintang juga harus masuk ke dalam: !(*.py). Juga, mungkin, jika OP hanya menginginkan file ".pdf" tersisa, maka file tanpa ekstensi juga harus dihapus dan tidak diabaikan.
Dennis Williamson
1
Pendekatan ini lebih sederhana dan lebih rapi daripada jawaban yang diterima.
Peter
18

Hapus ke sampah :

$ cd <the directory you want>
$ gvfs-trash !(*.pdf)

Atau melalui mvperintah (tetapi dengan cara ini Anda tidak dapat mengembalikannya dari Sampah karena tidak merekam informasi .trashinfo, jadi ini berarti Anda memindahkan file Anda ke tujuan di mana itu adalah sebagai berikut).

mv !(*.pdf) ~/.local/share/Trash/files
αғsнιη
sumber
6
Pendekatan ini jauh lebih aman daripada menggunakan langsung rm.
Seth
14

Pendekatan termudah: Buat direktori lain di suatu tempat (jika Anda hanya menghapus dalam satu direktori, tidak secara rekursif, itu bahkan bisa menjadi subdirektori); pindahkan semua .pdf di sana; hapus yang lainnya; pindahkan kembali pdf; hapus direktori perantara.

Cepat, mudah, Anda dapat melihat apa yang Anda lakukan. Pastikan direktori perantara ada di perangkat yang sama dengan direktori yang Anda bersihkan sehingga gerakannya adalah penggantian nama, bukan salinan!

Jerry
sumber
4
+1 Sekali lagi untuk komentar yang masuk akal bagi pengguna pemula, yang hampir pasti tidak akan menghasilkan penghapusan file secara tidak sengaja.
trognanders
4

Gunakan bash's GLOBIGNORE:

GLOBIGNORE=x.pdf:a.pdf
rm *
unset GLOBIGNORE

Dari halaman manual bash:

GLOBIGNORE:

            Daftar pola yang dipisahkan titik dua yang mendefinisikan set
            nama file yang akan diabaikan oleh ekspansi pathname.

Tes cepat:

mkdir /tmp/foooooo
cd /tmp/foooooo
touch x.pdf y.zip z.mp3 a.pdf
GLOBIGNORE=x.pdf:a.pdf
ls -1 *

Keluaran:

y.zip
z.mp3
Cyrus
sumber
3

Hati-hati dan menulis: gunakan xargs

Berikut ini pendekatan yang saya sukai, karena ini membuat saya sangat berhati-hati: menulis cara untuk menunjukkan file yang ingin saya hapus, kemudian mengirimkannya untuk rmdigunakan xargs. Sebagai contoh:

  • ls Tunjukkan semuanya
  • ls | grep pdfmenunjukkan kepada saya file yang ingin saya simpan. Hmm.
  • ls | grep -v pdfmenunjukkan yang sebaliknya: semua kecuali yang ingin saya simpan. Dengan kata lain, ini menunjukkan daftar hal yang ingin saya hapus. Saya dapat mengkonfirmasi ini sebelum melakukan sesuatu yang berbahaya.
  • ls | grep -v pdf | xargs rmmengirimkan daftar itu ke rmuntuk dihapus

Seperti yang saya katakan, saya terutama suka ini untuk keselamatan yang disediakannya: tidak disengaja rm *bagi saya. Dua keuntungan lain:

  • Ini komposable; Anda dapat menggunakan lsatau finduntuk mendapatkan daftar awal, seperti yang Anda inginkan. Anda dapat menggunakan apa pun yang Anda suka dalam proses mempersempit daftar itu - yang lain grep, beberapa awk, atau apa pun. Jika Anda hanya perlu menghapus file yang namanya mengandung warna, Anda dapat membuatnya dengan cara yang sama.
  • Anda dapat menggunakan setiap alat untuk tujuan utamanya. Saya lebih suka menggunakan finduntuk menemukan dan rmuntuk menghapus, daripada harus ingat yang findmenerima -deletebendera. Dan jika Anda melakukan ini, sekali lagi, Anda dapat membuat solusi alternatif; mungkin alih-alih rm, Anda bisa membuat trashperintah yang memindahkan file ke tempat sampah (memungkinkan "undeletion") dan menyalurkannya ke sana rm. Anda tidak perlu memiliki finddukungan opsi itu, Anda hanya perlu melakukannya.

Memperbarui

Lihat komentar oleh @pabouk untuk bagaimana memodifikasi ini untuk menangani beberapa kasus tepi, seperti jeda baris dalam nama file, nama file seperti my_pdfs.zip, dll.

Nathan Long
sumber
4
Saya perhatikan ada tiga masalah di sini: a) Ini akan mengecualikan file yang berisi pdfnamanya. --- b) Ini akan menghapus file PDF jika salah satu huruf dalam sufiks adalah huruf besar. --- c) Bukan ide yang baik untuk menggunakan output dari ls. Itu tidak akan bekerja dengan nama file yang mengandung baris baru. Beberapa implementasi lsganti karakter khusus misalnya tab oleh ?. --- Lebih baik untuk digunakan: find -maxdepth 1 -print0. (tidak sesingkat ls:) ----- Untuk menyelesaikan a) dan b) gunakan grep -vi '\.pdf$'--- solusi lengkap (tapi hanya GNU):find -maxdepth 1 -print0 | grep -viz '\.pdf$' | xargs -0 rm
pabouk
1
Saya mengerti bahwa Anda memaksudkan solusi sebagai proses "interaktif" dengan beberapa iterasi manual tetapi pemeriksaan akan sulit digunakan untuk daftar panjang file dan masalah yang disebutkan di atas dapat dengan mudah mengabaikan kesalahan.
pabouk
1
@ poabuk poin bagus; dunia nyata selalu memperumit banyak hal, dan koreksi Anda sangat membantu. :) Tapi saya masih berpikir pendekatan keseluruhan ini adalah yang terbaik. Jika ada terlalu banyak file untuk mengkonfirmasi semuanya secara visual, Anda dapat | head -20setidaknya melihat apakah itu terlihat benar, sedangkan jika Anda hanya rm my_pattern, Anda tidak memiliki kesempatan untuk menemukan kesalahan besar.
Nathan Long
1
Anda dapat menemukan file-file tersebut sebelum Anda menghapusnya juga, tinggalkan -delete dan gunakan saja find . -type f ! -name "*.pdf"untuk mencetak ke konsol, atau pipa ke file yang lebih sedikit atau. [dan kemudian pipa ke xargs ke rm jika diinginkan seperti komentar pabouk (dengan -print0 | ... -0 untuk nama file yang aneh)]
Xen2050
3

Saya biasanya memecahkan masalah seperti itu dari juru bahasa Python interaktif:

mic@mic ~ $ python
>>> import os
>>> for f in os.listdir('.'):
...   if not f.endswith('.pdf'):
...     os.remove(f)

Mungkin lebih lama dari satu-liner dengan findatau xargs, tapi itu sangat ulet, dan saya tahu persis apa yang dilakukannya, tanpa harus melakukan riset terlebih dahulu.

mic_e
sumber
Bagi mereka yang semakin gugup dengan setiap baris tambahan, kita bisa membuatnya menjadi satu:for item in [f for f in os.listdir('.') if not f.endswith('.pdf')]: os.remove(item)
Jacob Vlijm
python -c "import os; for f in os.listdir('.'): if not f.endswith('.pdf'): os.remove(f)"
mic_e
[os.remove(f) for f in os.listdir('.') if not f.endswith('.pdf')]
mic_e
bagus! yang kedua memberi saya kesalahan sintaks, tidak melihat mengapa.
Jacob Vlijm
aneh; ia bekerja dengan kedua python 3.4 dan python 2.7 di sistem saya.
mic_e
2

jawaban yang lebih baik (dibandingkan dengan jawaban saya sebelumnya) untuk pertanyaan ini adalah dengan menggunakan fileperintah yang kuat .

$ file -i abc.pdf
abc: application/pdf; charset=binary

sekarang masalahmu:

cd <the directory you want to search in>
for var in ./*
do
if file -i "$var" | grep -q 'application/pdf\;'
then
echo "$var"
fi
done

tugas dari forperintah adalah memberikan file dalam direktori saat ini dalam bentuk variabel $var. if-thenperintah menampilkan nama-nama file pdf dengan mengambil status keluar 0dari file -i "$var" | grep -q 'application/pdf\;'perintah, itu akan memberikan status keluar 0hanya jika menemukan file pdf.

Edward Torvalds
sumber
1
rm $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')

Peringatan! Lebih baik coba dulu

ls -l $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')
Martín-Blas Pérez Pinilla
sumber
2
Ugh, ini cacat dalam banyak hal: smallo.ruhr.de/award.html#ls , smallo.ruhr.de/award.html#grep , dan ini benar-benar mengabaikan nama file dengan spasi putih atau karakter khusus.
David Foerster
1
Anda harus benar-benar menggunakan -idengan grepuntuk pencocokan case-sensitive.
muru
1
rm -i -- !(*@(a|x).pdf)

Baca sebagai, hapus semua file yang bukan a.pdfatau x.pdf.

Ini bekerja dengan memanfaatkan 2 gumpalan diperpanjang, bagian luar !()untuk meniadakan gumpalan terkandung yang dengan sendirinya mengharuskan gumpalan harus cocok dengan satu atau lebih dari aatau xpola sebelum .pdfsufiks. Lihat glob # extglob .

$ ls -a
.dotfile1 .dotfile2 a.pdf x.pdf y.zip z.mp3

$ echo -- !(a.pdf)
-- x.pdf y.zip z.mp3

$ echo -- !(x.pdf)
-- a.pdf y.zip z.mp3

$ echo -- !(a.pdf|x.pdf)
-- y.zip z.mp3

$ echo -- !(@(a|x).pdf)   # NOTE.that this matches the .dotfiles* as well
-- . .. .dotfile1 .dotfile2 y.zip z.mp3

$ echo -- !(*@(a|x).pdf)  # but this doesn't
-- y.zip z.mp3

$ echo rm -i -- !(*@(a|x).pdf)
rm -i -- y.zip z.mp3
shalomb
sumber
1

cara shell portabel

$ ksh -c 'for i in ./*; do case $i in *.pdf)continue;; *)rm "$i";; esac;done'

Cukup banyak POSIX dan kompatibel dengan Bourne-gaya shell ( ksh, bash, dash). Sangat cocok untuk skrip portabel dan ketika Anda tidak dapat menggunakan bashglobbing shell diperpanjang.

perl:

$ perl -le 'opendir(my $d,"."); foreach my $f (grep(-f && !/.pdf/ , readdir($d))){unlink $f};closedir $d'                                                             

Atau sedikit lebih bersih:

$ perl -le 'opendir(my $d,"."); map{ unlink $_ } grep(-f "./$_" && !/.pdf/ , readdir($d));closedir $d'

python alternatif

python -c 'import os;map(lambda x: os.remove(x), filter(lambda x: not x.endswith(".pdf"),os.listdir(".")))'
Sergiy Kolodyazhnyy
sumber
0

Berhati-hatilah dengan apa yang Anda hapus!

Cara aman untuk mengujinya sebelum mencoba menghapus adalah mengujinya terlebih dahulu ls, karena beberapa perilaku yang tidak tertangkap dapat menghapus file yang tidak diinginkan. Dan Anda dapat melakukannya langsung di luar direktori. lsmirip dengan rm, jadi:

ls sub/path/to/files/!(*.pdf)

Ini akan mencantumkan

y.zip
z.mp3

Dan sekarang Anda dapat melihat apa yang Anda hapus dan dapat menghapusnya dengan aman:

rm sub/path/to/files/!(*.pdf)

Dan itu saja. Anda dapat menggunakan wildcard *untuk menjadi lebih selektif seperti hanya menyimpan dokumen kursus pemrograman:

rm sub/path/to/files/!(*programming*)
KeitelDOG
sumber