Kecualikan dari * dalam baris perintah

14

Ada banyak situasi di mana penggunaan a *hampir tidak dapat dihindari - misalnya rm -rf *dalam folder yang menampung ribuan subfolder dan file.

Tetapi bagaimana jika Anda ingin mengecualikan hanya satu atau dua file atau folder dari rmperintah? Saya sudah menelusuri Google dan hanya menemukan solusi yang cukup rumit seperti yang find . -depth -not \( -name 'one' -o -name 'two' \ -o -name 'three' \) -exec rm {} \;dinyatakan di sini .

Apakah ada kemungkinan untuk melakukan ini dengan cara yang lebih mudah - tanpa jalan memutar ke sana find? Misalnya rm -rf --exclude='one' --exclude='two' --exclude='three' *suka di rsync atau hanya rm -rf -e 'one','two','three' *?

Mungkin bahkan kemungkinan umum untuk mengecualikan hal-hal dari *(perintah sehingga lainnya seperti cp, mv, ... tidak harus melaksanakan sendiri)? Sesuatu seperti *{'one','two','three'}itu?

David
sumber
3
Jika Anda ingin menyimpan hanya satu atau dua file maka cara termudah dan termudah adalah memindahkan file-file itu ke lokasi lain, bersihkan semua file yang tersisa dan pindahkan yang Anda simpan kembali. Jika Anda ingin menyimpan lebih banyak file yang akan saya gunakan finddengan --deleteopsi (tidak perlu mengeksekusi rmuntuk setiap file. Itu overhead yang tidak perlu).
Hennes
Itu akan menjadi suatu kemungkinan, tetapi hampir sama rumitnya dengan yang saya sebutkan. Saya tahu saya bisa menggabungkan perintah ke sesuatu seperti mv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/three, tapi saya lebih suka solusi yang memberikan kemungkinan untuk secara eksplisit mengecualikan sesuatu dari *. Pasti akan ada situasi di mana memindahkan atau menyalin file ke tujuan lain tidak akan menjadi pilihan.
David
2
Itu sebabnya saya tidak mempostingnya sebagai jawaban. Hanya sebagai cara yang tidak terlalu rumit dalam melakukan sesuatu (tiga perintah yang sangat sederhana vs satu yang agak lebih rumit). Saya benci kompleksitas dalam kombinasi dengan rm.
Hennes

Jawaban:

33

Untuk Bash ada opsi shell yang disebut extglobyang dinonaktifkan secara default untuk menjaga kompatibilitas dengan sintaks shell standar. Extglob melengkapi sintaks oleh operator tambahan seperti !(), ?(), @()dan beberapa lagi.

Untuk menghidupkan extglob, ketikkan shopt -s extglob. Untuk mengaktifkannya untuk tipe pengguna saat ini echo 'shopt -s extglob' >> ~/.bashrc.

Untuk contoh rm: Dengan extglobAnda dapat menggunakan

rm -rf !(one|two|three)
choroba
sumber
Tampaknya ini solusi yang bagus, tetapi saya juga tidak ingin menghidupkan dan mematikannya setiap kali saya menghadapi situasi seperti yang dijelaskan.
David
3
@ David: Anda bisa memasukkan shoptbenda itu ke dalam diri Anda ~/.bashrc, tidak ada salahnya.
enzotib
Jika saya melakukannya dengan benar, opsi extglob tidak mengubah efek *sama sekali, tetapi malah menambahkan indikator serupa (seperti !) yang memiliki fungsi tambahan? Dalam hal ini akan menjadi solusi yang bagus.
David
1
@ David: Tepat.
choroba
1
@ David Untuk menjaga kompatibilitas dengan sintaks shell standar . Ada kemungkinan situasi lain di mana perilaku dapat berubah.
gerrit
4

Saya telah membangun kecuali untuk itu (sry bahwa saya belum dapat menambahkan dokumentasi).

Saya punya folder seperti ini:

$ ls
a b c d

Mengetik except b dakan memberi Anda adanc

$ except b d
ac

Sekarang Anda dapat menyalurkannya ke rm.

except b d | xargs -0 rm

Ini mungkin sulit untuk diingat, jadi mengapa tidak membuat skrip di atas kecuali, yang disebut rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

Demikian pula mudahnya adalah ls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls
Metode Penolong
sumber
3
Ini bagus, tetapi tidak benar-benar diperlukan ketika Anda tahu tentang extglob - askubuntu.com/a/329233/138369
David
1

Sebagai alternatif untuk extglob (meskipun itu adalah jawaban yang sangat bagus dan semua orang seharusnya ada shopt -s extglob globstardi .bashrc), Anda dapat menggunakan variabel global $ GLOBIGNORE . Katakanlah Anda ingin mendapatkan setiap file kecuali 'foo.txt' dan 'bar baz.txt':

GLOBIGNORE=foo.txt:'bar baz.txt'

... Namun, ini akan mengaktifkan opsi shell dotglob, yang berarti *akan cocok dengan file yang dimulai dengan titik (yang biasanya disembunyikan). Jadi Anda sebenarnya membutuhkan dua perintah:

GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob

Karena ini adalah variabel global, itu akan memengaruhi setiap bola yang Anda gunakan sampai $ GLOBSTAR tidak disetel baik dengan keluar atau dengan

GLOBIGNORE=

Ini juga hanya akan bekerja pada string literal yang diteruskan ke variabel. Anda dapat melihat apa yang saya maksud dengan menetapkan $ GLOBIGNORE dan melihat perbedaan antara perintah-perintah ini:

printf '%s\n' *
printf '%s\n' ./*
Evilsoup
sumber