Hapus semua file kecuali beberapa dari direktori

215

Saat menggunakan sudo rm -r, bagaimana saya bisa menghapus semua file, dengan pengecualian sebagai berikut:

textfile.txt
backup.tar.gz
script.php
database.sql
info.txt
masjoko
sumber
5
Kedengarannya seperti pertanyaan untuk unix.stackexchange.com
Jason
1
Ada 2 cara untuk membaca pertanyaan ini, dan jawaban yang ada mencakup kedua interpretasi: BAIK: (a) menyimpan file dengan nama yang ditentukan secara langsung terletak di direktori target dan - seperti yang rm -rtersirat - hapus semua yang lain, termasuk subdirektori - bahkan jika mengandung file dengan nama yang ditentukan; ATAU: (b) melintasi seluruh subtree dari direktori target dan, di setiap direktori, hapus semua file kecuali yang memiliki nama yang tercantum.
mklement0

Jawaban:

219
find [path] -type f -not -name 'textfile.txt' -not -name 'backup.tar.gz' -delete

Jika Anda tidak menentukan, -type ftemukan juga akan mendaftar direktori, yang mungkin tidak Anda inginkan.


Atau solusi yang lebih umum menggunakan kombinasi yang sangat berguna find | xargs:

find [path] -type f -not -name 'EXPR' -print0 | xargs -0 rm --

misalnya, hapus semua file non txt di direktori saat ini:

find . -type f -not -name '*txt' -print0 | xargs -0 rm --

The print0dan -0kombinasi diperlukan jika ada ruang di salah satu nama file yang harus dihapus.

awi
sumber
2
tampaknya xargs memiliki batas ukuran teks
frazras
26
untuk menghapus beberapa pola:find . -type f -not -name '*ignore1' -not -name '*ignore2' | xargs rm
Siwei Shen 申思维
5
bagaimana dengan direktori? itu akan menghapus semua file, tetapi apakah itu menghapus folder ?!
orezvani
31
Alih-alih "| xargs rm", find mengambil parameter -delete.
Emil Stenström
1
alih-alih -print0 | xargs -0 rm --Anda hanya bisa-delete
Andy
150
rm !(textfile.txt|backup.tar.gz|script.php|database.sql|info.txt)

Extglob (Pencocokan Pola yang Diperluas) harus diaktifkan dalam BASH (jika tidak diaktifkan):

shopt -s extglob
pl1nk
sumber
2
Saya mendapatkan "kesalahan sintaksis dekat token yang tidak terduga` ('"ketika saya melakukannya shopt -s extglob; rm -rf !(README|LICENSE). Ada gagasan mengapa?
Dennis
@saya tidak ingat, maaf. Saya mungkin akhirnya menggunakan finddengan -deletepilihan.
Dennis
Ini adalah solusi terbaik untuk saya dan berfungsi secara default di Ubuntu 12.04.4 LTS saya tanpa perlu
shopt
3
@nic, @Dennis: Kesalahan sintaksis menunjukkan bahwa sesuatu yang LAIN daripada bashyang digunakan, misalnya dash, di mana extglobtidak didukung. Namun, dalam shell interaktif bash , perintah juga JUGA tidak akan berfungsi seperti yang dinyatakan, meskipun untuk alasan yang berbeda. Singkatnya: jalankan shopt -s extglobDENGAN SENDIRI; SEKALI SUKSES ENABLED (memverifikasi dengan shopt extglob), mengeksekusi rm -rf !(README|LICENSE). (Meskipun extglobbelum diaktifkan, !(...)dievaluasi oleh ekspansi sejarah SEBELUM perintah dieksekusi; karena ini kemungkinan gagal, perintah extglob
NEITHER
2
@nic, @Dennis, @ mklement0: Saya memiliki masalah yang sama dengan "kesalahan sintaksis dekat token tak terduga (" ketika menjalankan perintah dalam file .sh (#! / bin / bash) tetapi tidak ketika saya menjalankannya dalam perintah antarmuka baris. Ternyata selain shopt -s extglobmenjalankan dalam antarmuka baris perintah, saya harus menjalankannya kembali di dalam file skrip saya. Ini memecahkan masalah bagi saya.
Ahresse
41

find . | grep -v "excluded files criteria" | xargs rm

Ini akan mencantumkan semua file di direktori saat ini, kemudian daftar semua yang tidak sesuai dengan kriteria Anda (waspadalah dengan nama direktori yang cocok) dan kemudian hapus.

Pembaruan : berdasarkan hasil edit Anda, jika Anda benar-benar ingin menghapus semuanya dari direktori saat ini kecuali file yang Anda daftarkan, ini dapat digunakan:

mkdir /tmp_backup && mv textfile.txt backup.tar.gz script.php database.sql info.txt /tmp_backup/ && rm -r && mv /tmp_backup/* . && rmdir /tmp_backup

Ini akan membuat direktori cadangan /tmp_backup(Anda punya hak root, kan?), Pindahkan file yang Anda daftarkan ke direktori itu, hapus semua secara rekursif dalam direktori saat ini (Anda tahu bahwa Anda berada di direktori yang benar, bukan?), Pindah kembali ke direktori sekarang semuanya dari /tmp_backupdan akhirnya, hapus /tmp_backup.

Saya memilih direktori cadangan untuk di-root, karena jika Anda mencoba menghapus semuanya secara rekursif dari root, sistem Anda akan memiliki masalah besar.

Tentunya ada cara yang lebih elegan untuk melakukan ini, tetapi yang ini cukup mudah.

darioo
sumber
2
Bekerja dengan sangat baik dengan egrep, misalnya untuk membersihkan file lateks antara:find . | egrep -v "\.tex|\.bib" | xargs rm
mtsz
perintah luar biasa! untuk menghapus direktori, ubah saja ke rm -r
Aris
1
Jika Anda hanya ingin menghapus semua yang ada di direktori saat ini selain kriteria yang dikecualikan: find . -maxdepth 1 | grep -v "exclude these" | xargs rm -rbekerja lebih cepat karena tidak perlu menelusuri direktori yang tidak perlu.
billynoah
Re findpipeline: mengesampingkan masalah efisiensi (3 perintah digunakan untuk apa yang findbisa dilakukan sendiri), ini tidak akan berfungsi sebagaimana dimaksud dengan nama file dengan ruang yang disematkan dan berpotensi akan menghapus file yang salah.
mklement0
Perintah yang menyimpan file "yang akan disimpan" di lokasi lain ( /tmp_backup) tidak berakhir dengan baik jika terganggu — dari perspektif pengguna, semua file telah lenyap, kecuali mereka tahu ke mana harus mencari mereka untuk mendapatkannya kembali . Karena itu saya tidak mendukung strategi jenis ini.
Craig McQueen
20

Dengan asumsi bahwa file dengan nama-nama itu ada di banyak tempat di pohon direktori dan Anda ingin menyimpan semuanya:

find . -type f ! -regex ".*/\(textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt\)" -delete
Dijeda sampai pemberitahuan lebih lanjut.
sumber
19

Anda dapat menggunakan variabel lingkungan GLOBIGNORE di Bash.

Misalkan Anda ingin menghapus semua file kecuali php dan sql, maka Anda dapat melakukan hal berikut -

export GLOBIGNORE=*.php:*.sql
rm *
export GLOBIGNORE=

Pengaturan GLOBIGNORE seperti ini mengabaikan php dan sql dari wildcard yang digunakan seperti "ls *" atau "rm *". Jadi, menggunakan "rm *" setelah mengatur variabel hanya akan menghapus file txt dan tar.gz.

yang paling keras
sumber
6
+1; Alternatif yang lebih sederhana untuk mengatur dan mengembalikan GLOBIGNOREvariabel adalah dengan menggunakan subkulit:(GLOBIGNORE='*.php:*.sql'; rm *)
mklement0
19

Saya lebih suka menggunakan daftar permintaan sub:

rm -r `ls | grep -v "textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt"`

-v, --invert-match pilih jalur yang tidak cocok

\| Pemisah

Nick Tsai
sumber
1
Solusi elegan
Joshua Salazar
9

Karena tidak ada yang menyebutkannya:

  • salin file yang tidak ingin Anda hapus di tempat yang aman
  • hapus semua file
  • pindahkan file yang disalin kembali ke tempatnya
gniourf_gniourf
sumber
2
Ini lebih rumit karena Anda harus mengurus izin setelah Anda menyalinnya kembali.
Romulus
2
@RemusAvram Anda dapat menggunakan sakelar yang sesuai dengan cpatau rsyncuntuk mempertahankan izin. Bagaimanapun, ini hanyalah metode alternatif (diberikan sebagai saran) yang memiliki tempatnya di sini, sebagai jawaban untuk OP.
gniourf_gniourf
Ini mungkin tidak sesuai dalam banyak situasi di mana file yang disimpan secara aktif digunakan oleh proses lain. Itu juga rumit dari file yang akan dihapus hanya sebagian kecil dan sejumlah besar file yang terlibat.
codeforester
@ kodeforester: tentu. Tapi ada situasi di mana itu pantas, meskipun ... sebenarnya saya tidak benar-benar mengerti maksud dari komentar Anda :).
gniourf_gniourf
6

Anda dapat menulis untuk loop untuk ini ...%)

for x in *
do
        if [ "$x" != "exclude_criteria" ]
        then
                rm -f $x;
        fi
done;
mishunika
sumber
6

Sedikit terlambat untuk OP, tapi semoga bermanfaat bagi siapa saja yang tiba di sini nanti oleh google ...

Saya menemukan jawabannya oleh @awi dan mengomentari -delete oleh @Jamie Bullock sangat berguna. Utilitas sederhana sehingga Anda dapat melakukan ini di direktori yang berbeda dengan mengabaikan nama / tipe file yang berbeda setiap kali dengan mengetik minimal:

rm_except (atau apa pun yang Anda ingin beri nama)

#!/bin/bash

ignore=""

for fignore in "$@"; do
  ignore=${ignore}"-not -name ${fignore} "
done

find . -type f $ignore -delete

mis. untuk menghapus semuanya kecuali file teks dan foo.bar:

rm_except *.txt foo.bar 

Mirip dengan @mishunika, tetapi tanpa klausa if.

lhuber
sumber
5

Jika Anda menggunakan zshyang sangat saya rekomendasikan.

rm -rf ^file/folder pattern to avoid

Dengan extended_glob

setopt extended_glob
rm -- ^*.txt
rm -- ^*.(sql|txt)
sudo bangbang
sumber
4

Mencoba ini berhasil:

rm -r !(Applications|"Virtualbox VMs"|Downloads|Documents|Desktop|Public)

tetapi nama dengan spasi sulit (seperti biasa). Mencoba juga dengan Virtualbox\ VMskutipan. Itu selalu menghapus direktori itu ( Virtualbox VMs).

juanfal
sumber
TIDAK berfungsi untuk file. rm !(myfile.txt)menghapus semua termasukmyfile.txt
khaverim
harus dieksekusi shopt -s extglobpertama seperti yang ditunjukkan oleh @ pl1nk
zhuguowei
Ya, sangat penting untuk mengaktifkan extended globbing ( extglob ) di bash, saya melakukan ini dalam rutinitas harian, lebih dari beberapa Mac di laboratorium: shopt -s extglobdan kemudian cd /Users/alumno/dan akhirnya rm -rf !(Applications|Virtualbox*VMs|Downloads|Documents|Desktop|Public|Library) Baca tentang globbing yang diperluas di sini
juanfal
4

Hanya:

rm $(ls -I "*.txt" ) #Deletes file type except *.txt

Atau:

rm $(ls -I "*.txt" -I "*.pdf" ) #Deletes file types except *.txt & *.pdf
wyx
sumber
Ini tidak disarankan. Parsing lsoutput bisa menjadi masalah. Lihat Di Sini untuk informasi terperinci.
RJ
2

Buat file tidak berubah. Bahkan root tidak akan diizinkan untuk menghapusnya.

chattr +i textfile.txt backup.tar.gz script.php database.sql info.txt
rm *

Semua file lain telah dihapus.
Akhirnya, Anda dapat mengatur ulangnya agar dapat berubah.

chattr -i *
L'alligator des abers
sumber
0

Ini mirip dengan komentar dari @ siwei-shen tetapi Anda perlu -obendera untuk melakukan beberapa pola. The -obendera singkatan dari 'atau'

find . -type f -not -name '*ignore1' -o -not -name '*ignore2' | xargs rm

Daniel Chen
sumber
0

Anda dapat melakukan ini dengan dua urutan perintah. Pertama-tama tentukan array dengan nama file yang tidak ingin Anda kecualikan:

files=( backup.tar.gz script.php database.sql info.txt )

Setelah itu, loop semua file dalam direktori yang ingin Anda kecualikan, memeriksa apakah nama file dalam array yang tidak ingin Anda kecualikan; jika tidak maka hapus file tersebut.

for file in *; do
  if [[ ! " ${files[@]} " ~= "$file"  ]];then
    rm "$file"
  fi
done
Leonardo Hermoso
sumber
Perbandingan regex tidak benar - ini akan mempertahankan file apa pun yang namanya adalah substring dari salah satu file yang dilindungi (meskipun ruang di sekitarnya agak mengurangi itu; tetapi nama file dapat berisi spasi). Anda harus mengumpulkan array dari semua file, lalu kurangi file yang ingin Anda kecualikan dari array itu, lalu hapus file yang tersisa.
tripleee
-1

Karena belum ada yang menyebutkan ini, dalam satu kasus tertentu:

OLD_FILES=`echo *`
... create new files ...
rm -r $OLD_FILES

(atau adil rm $OLD_FILES)

atau

OLD_FILES=`ls *`
... create new files ...
rm -r $OLD_FILES

Anda mungkin perlu menggunakan shopt -s nullglobjika beberapa file ada di sana atau tidak di sana:

SET_OLD_NULLGLOB=`shopt -p nullglob`
shopt -s nullglob
FILES=`echo *.sh *.bash`
$SET_OLD_NULLGLOB

tanpa nullglob, echo *.sh *.bashdapat memberi Anda "a.sh b.sh * .bash".

(Setelah mengatakan semua itu, saya sendiri lebih suka jawaban ini , meskipun tidak bekerja di OSX)

18446744073709551615
sumber
Jawaban yang ada oleh Leonardo Hermoso melakukan ini dengan bug yang lebih sedikit. Ini akan gagal untuk nama file dengan spasi; menggunakan array secara elegan memecahkan masalah tertentu (dan juga yang kurang penting menghindari penggunaan huruf besar untuk variabel pribadinya, yang umumnya harus dihindari).
tripleee
-3

Daripada menggunakan perintah langsung, silakan pindahkan file yang diperlukan ke temp dir di luar dir saat ini. Kemudian hapus semua file menggunakan rm *atau rm -r *.

Kemudian pindahkan file yang diperlukan ke direktori saat ini.

Ajeesh
sumber
Ini mungkin tidak sesuai dalam banyak situasi di mana file yang disimpan secara aktif digunakan oleh proses lain. Itu juga rumit dari file yang akan dihapus hanya sebagian kecil dan sejumlah besar file yang terlibat.
codeforester
-3

Hapus semuanya kecuali file.name:

ls -d /path/to/your/files/* |grep -v file.name|xargs rm -rf
mik-mak
sumber
2
Ini akan gagal total dan dapat menyebabkan kehilangan data jika direktori / file Anda memiliki spasi atau karakter yang tidak biasa. Anda seharusnya tidak menguraikan ls. mywiki.wooledge.org/ParsingLs
RJ