`[!]` (parantez içinde ünlem işareti) bash içinde joker karakter


10

Globbing desenleri ve joker karakterlerle karşılaştım ve özellikle ilgimi çekti [!].

Bu yapı, yapıya benzer [!], köşeli parantez içindeki herhangi bir karakteri eşleştirmek yerine, [ve arasında listelenmediği sürece herhangi bir karakterle eşleşir ].

rm myfile [!192]

Yukarıdaki inanıyorum yukarıdaki 192 adında herhangi bir / tüm dosyaları dışında herhangi bir / tüm dosyaları kaldıracaktır.

Ancak bunun bir dosya uzantısı ve özellikle çoklu koşullar ile doğru kullanımı hakkında endişeliyim.

Böyle bir durumda uygun sözdizimi ne olur?

rm myfile [!.gif .csv. mp3] 

veya

rm myfile [!.gif !.csv !.mp3]

Benim endişem nokta yanlış yerleştirilmiş olabilir, ve böylece (ki kesinlikle bunlardan herhangi biri olacak?) İle herhangi bir dosya .belirli dosyaları manipüle neden olmak için çalışırken, manipüle olurdu.

Bu yapı, yapıya benzer [ ], köşeli parantez içindeki herhangi bir karakteri eşleştirmek yerine, [ve arasında listelenmediği sürece herhangi bir karakterle eşleşir ].

( http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm adresinden alıntılanmıştır )

Şimdi; bana göre o zaman tekil !bir yeterlidir ve bundan sonraki tüm değerler aralık dahilindedir.


5
genel bir ipucu olarak, ls my-patternecho my-commandrm
koşmadan

Ayrıca, çoğunda olmasa da birçok dosyanın uzantıları olmadığını unutmayın. Linux sistemlerindeki uzantılar genellikle önemsizdir ve program türlerinin çoğu tarafından dosya türünü tanımlamak için kullanılmaz.
terdon

2
İlk alıntı yanlış olduğunu unutmayın, şimdi temelde [!]benzer ama değil gibi görünüyor[!]
ilkkachu

2
Bu bağlamdakiyle ^aynı anlama sahip olduğundan bahsetmeye değer !, bu nedenle tam olarak olduğu gibi [^a]hariç tutulur : [karakterini izleyen ilk karakter bir! veya bir ^ varsa, kapalı olmayan herhangi bir karakter eşleştirilir. ( )a[!a]man bash
tatlı

3
rm myfile [!192]kaldıracak myfile, hem de herhangi bir tek karakterlik dosya adında değil 1, 9ya 2.
Kevin

Yanıtlar:


16

Alıntı man 7 glob:

An  expression  "[!...]"  matches  a single character, namely any character
that is not matched by the expression obtained by removing the first '!' from it.
(Thus, "[!]a-]" matches any single character except ']', 'a' and '-'.)

Buradaki tek karakter kısmına vurgu, []Bash Pattern Matching'deki tüm dizeler için kullanılamaz.


bash's Brace Expansion , dizeleri eşleştirmek için kullanılabilir, ancak bunları reddetmenin bir yolu yoktur (biliyorum). Örneğin

1.{gif,csv,mp3}

genişletildi

1.gif 1.csv 1.mp3

bu dosyalar mevcut olsun ya da olmasın.


Wchargin'in işaret ettiği gibi shopt, biri genişletilmiş desen eşleştirme operatörlerini etkinleştirmek için kullanılabilir !(). Çalıştırdıktan sonra shopt -s extglob, ör.

1.!(gif|csv|mp3)

genişletildi

1.jpg 1.bmp 1.png

bu dosyalar geçerli dizinde mevcutsa. Daha fazla okuma için man bash(bkz GENLEŞME / yol adı Genişleme) ve yol adı genişleme (globbing) .


Yapmaya çalıştığınız şey için her zaman kullanırım find, bu olumsuzluk ve çok daha fazlasını sunar, örneğin:

find . -maxdepth 1 -type f ! -name "*.gif" -a ! -name "*.csv" -a ! -name "*.mp3"

Bu sadece bulguları listeler, bunları kaldırmak istiyorsanız sadece komutun sonuna-delete seçeneği ekleyin . Her zaman olduğu gibi kaldırma eylemleriyle: Her zaman önce dikkatlice kontrol edin .

findçok iyi açıklanmış tonlarca faydalı seçenek sunar man find.


1
Bu findkomutun özyinelemeli olduğunu unutmayın ( düzenle: başlangıçta gösterildiği gibi - Bu yorumu ilettiği ilgili ancak önemli olmayan ek bilgiler için saklayabilirim). Yalnızca geçerli dizinde bulunan ancak alt dizinlerinde bulunmayan dosyaları bulmak ve böylece yalnızca -deleteeylem eklenirse bu dosyaları silmek - -maxdepth 1hemen sonra ekleyebilirsiniz find .. globstarKabuk seçeneği açık olmadığı ve **kullanılmadığı sürece globlar Bash'de tekrarlayıcı olmadığından, bu, globlamanın etkisi ile aynı hizaya getirecektir . Soruda gösterilen glob tüm kabuklarda tekrarlayıcı değildir.
Eliah Kagan

2
"Onları etkisiz hale getirmesine hiçbir şekilde (benim bildiğim) yoktur" -with shopt -s extglobkullanabileceğiniz echo 1.!(gif|csv|mp3)tüm yazdıran 1.extnerede extdeğil gif, csvya mp3. (Bkz. "Kalıp Eşleştirme" alt bölümü man bash.)
wchargin

10

Sanırım parantez kullanımını yanlış anladınız. [abc]maçları tek karakter a, bya da c. [!abc]maçları biri olan karakter değil a , bya da c. Yani kommand

rm myfile [192]

kaldıracak myfileve dosyalar 1, 9ve 2sen Dosyam ile dirsek arasına bir boşluk var çünkü. Buna karşılık, komut

rm myfile[192]

dosyaları kaldıracak myfile1, myfile9ve myfile2.


doğru. Kullanmak daha iyi find.
pLumo

Aslında [] bir aralık için kullanılır, [!] Tek bir karakter
IronUhlan

1
@IronUhlan, ikisi de bir aralık veya karakter listesi alıyor. Sadece diğeri bir olumsuzlama.
ilkkachu

7

Köşeli ayraçlar [ ]bir karakter sınıfını gösterir. İçlerindeki herhangi bir karakter verilen konumda eşleşebilir. Yani

file[192]

üç olası adla eşleşir:

file1
file2
file9

O mu değil maç file192yalnızca sonra tek karakteri ile ilgilendiği için, file.

Aralıkları parantez içinde de kullanabiliriz. [0-9]verilen konumdaki herhangi bir rakam ile eşleşir. İki basamak için ihtiyacımız var [0-9][0-9]. Bir rakam ve ardından büyük bir harf için [0-9][A-Z]...

! olumsuzlamayı belirtir.

file[!192]

sonra herhangi bir tek karakteri sahip dosyaları maç olacak fileharicinde 1ya 9ya 2. Örneğin, eşleşecek file7ve filesve file%(ve diğerleri) eşleşmeyecektir , ancak bunun file192 fazladan iki karakteri vardır.

Kullanım durumunuz için findbunun yerine kullanmanızı öneririm [!]. Bir notoperatörü var.

Ayrıca özyinelemeli , yani varsayılan olarak tüm alt dizinlere gider. İstediğiniz -maxdepth 1buysa, dizini geçerli diziyle sınırlayabilirsiniz . Kabuk joker karakterleri (globbing) yinelemeli değildir (yinelemeli globbing **etkinleştirilebilir).

Sembolik bağlantıları silmekten kaçınmak istemediğinizi varsayarsak, -type dherhangi bir dizini silmekten kaçınmak için reddedilen testleri ekleyebiliriz . Eliah Kagan'a bu öneri için teşekkürler.

find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \)

Ne istediğinizi görürseniz, komutu yeniden çalıştırabilirsiniz. -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete

2
Ve sanırım aynı anda birkaç aralık da yapabilirsiniz. Örneğin, onaltılık basamak[0-9a-fA-F]
Wilson

@Zanna, evet! Aslında find kullanıyordum :) Bir "değil" operatörü olduğunu fark
etmedim
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.