Unix - hapus file dan folder tidak termasuk POLA

9

Baru-baru ini saya dihadapkan pada tugas untuk menghapus semua file / folder dalam direktori tidak termasuk yang cocok dengan pola tertentu. Jadi saya membuat perintah unix baris tunggal untuk melakukan pekerjaan. Haruskah hanya satu baris? Saya kira tidak, tapi pasti lebih dingin seperti itu!

Walaupun masalahnya cukup sederhana, saya sedikit terkejut melihat betapa rumitnya solusi saya. Inilah perintah yang saya gunakan; CATATAN: ini adalah solusi yang buruk karena tidak menangani nama file yang mengandung karakter umpan baris (yang tidak masalah dalam situasi saya).

ls | grep -v PATTERN | xargs -n1 -IREPLACE rm -rf REPLACE

Saya tidak menggunakan perintah "find" karena saya tidak ingin recurse ke folder yang cocok dengan POLA. Misalnya, pertimbangkan struktur file berikut:

file_foo.txt
first_dir
  |
  +--> contents
  +--> ...
foo_dir
  |
  +--> anotherfile.txt
  +--> morefiles.log
foo_file.txt
somefile.txt

Menggunakan pola "foo" hanya harus menghapus "first_dir" (dan isinya tentu saja) dan "somefile.txt" ( tidak "anotherfile.txt" atau "morefiles.log").

Pertanyaan, adakah cara yang lebih baik (lebih elegan dan benar) untuk mencapai ini?


EDIT:
Baru-baru ini menarik perhatian saya bahwa "menemukan" mungkin a lebih baik pilihan:

find * -maxdepth 0 ! -name PATTERN -print0 | xargs -0n1 rm -rf

Solusi ini tidak menangani jalur yang berisi karakter umpan baris dengan benar.

joxl
sumber
EDIT: berubah "the_dir" salah untuk memperbaiki "first_dir".
joxl

Jawaban:

9

Contoh-contoh berikut miliki echo diawali sehingga Anda dapat menguji pola sebelum benar-benar menggunakannya. Hapus echo untuk mengaktifkan rm -rf. Pengganti rm -ri untuk meminta konfirmasi.

ksh memiliki ekstensi kecocokan negatif untuk globbing-nya:

# ksh
echo rm -rf !(*foo*)

Sintaks yang sama tersedia di pesta jika Anda mengatur extglob pilihan:

# bash
shopt -s extglob
echo rm -rf !(*foo*)

zsh memiliki sintaksnya sendiri untuk ini:

# zsh
setopt extended_glob
echo rm -rf ^*foo*

Itu juga bisa menggunakan ksh sintaks gaya:

# zsh: ksh-style glob syntax
setopt ksh_glob no_bare_glob_qual
echo rm -rf !(*foo*)

# zsh: ksh-style glob syntax, adapted for the default bare_glob_qual option
setopt ksh_glob bare_glob_qual
echo rm -rf (!(*foo*))
Chris Johnsen
sumber
Sederhana, anggun, menangani karakter linefeed, (hampir) satu baris (saya telah menambahkan shopt -s extglob ke file .bashrc saya, jadi sekarang aku s satu baris). Sempurna! Saya berjanji suara positif segera setelah reputasi saya memungkinkan.
joxl
7

Ini satu lagi find larutan. Saya tidak yakin ini memiliki keunggulan nyata atas Anda, tetapi tidak perlu xargs dan memungkinkan untuk kemungkinan langka * yang diperluas ke terlalu banyak nama.

find . -maxdepth 1 ! -name PATTERN -type f -delete

Saya juga menambahkan -type f sehingga tidak akan mencoba untuk menghapus direktori.

Peringatan: -delete sangat kuat. Saya memberikan salah satu file tes saya 0 izin dan perintah di atas menghapusnya tanpa ragu.

garyjohn
sumber
6
Tes dulu. Menggantikan -delete dengan -print untuk melihat apakah itu menemukan file yang Anda inginkan. Jika semuanya baik, gunakan riwayat perintah (mis. Tekan tombol atas) untuk kembali ke perintah sebelumnya untuk memastikan bahwa Anda bekerja dengan filter pencarian yang sama.
Doug Harris
Baca dengan cermat, perhatikan bahwa saya melakukan ingin menghapus direktori. Dan find -delete tidak akan menghapus direktori yang tidak kosong. Perhatikan juga itu find -maxdepth 1 cocok dengan "." (direktori saat ini) yang sangat buruk. Meskipun saya suka ide Anda menghilangkan xargs, mungkin digunakan find -exec kalau tidak. find * -maxdepth 0 ! -name PATTERN -exec rm -rf '{}' \;
joxl
0

Anda dapat menggunakan contoh skrip rebol ini http://askcodegeneration.com/keep-subversion/ alih-alih yang jauh lebih dimengerti dan yang dapat mengatur interaksi pengguna seperti pemilihan dan konfirmasi folder:

delete-file: func[file][
    if/else none? find file "/.svn/" [
        delete file
    ][
        print ["keeping" file]
    ]
]
dir: request-dir
ans: ask rejoin ["Do you confirm " dir "? (Yes): "]
if (ans = "Yes") [
    foreach-file dir :delete-file
]
Rebol Tutorial
sumber