`[!]` (wykrzyknik w nawiasach) symbol zastępczy w bash

10

Natknąłem się na wzory globowania i symbole wieloznaczne, a szczególnie mnie interesuje [!].

Konstrukcja ta jest podobna do [!]konstrukcji, z wyjątkiem tego, że zamiast dopasowywać dowolne znaki w nawiasach, będzie pasować do dowolnego znaku, o ile nie będzie wymieniony między [a ].

rm myfile [!192]

Powyższe wierzę, że usunie wszystkie / wszystkie pliki, z wyjątkiem jakichkolwiek / wszystkich plików, które mają 192 w nazwie.

Niepokoi mnie jednak prawidłowe użycie tego rozszerzenia pliku, aw szczególności wielu warunków.

Jaka byłaby właściwa składnia w takiej sytuacji?

rm myfile [!.gif .csv. mp3] 

lub

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

Martwię się, że kropka może zostać zgubiona, a więc każdy plik z .(który z nich by był na pewno?) Zostałby zmanipulowany, gdy chcę spowodować manipulację konkretnymi plikami.

Konstrukcja ta jest podobna do [ ]konstrukcji, z wyjątkiem tego, że zamiast dopasowywać dowolne znaki w nawiasach, będzie pasować do dowolnego znaku, o ile nie będzie wymieniony między [a ].

(cytowany z http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm )

Teraz; dla mnie, co sugeruje, że liczba pojedyncza !jest wystarczająca, a wszystkie wartości w nim zawarte są zawarte w zakresie.

IronUhlan
źródło
5
jako ogólną wskazówkę, użyj ls my-patternlub, echo my-commandaby sprawdzić, czy globowanie jest wykonywane tak, jak chcesz, przed uruchomieniemrm
Wayne_Yux
Pamiętaj też, że wiele, jeśli nie większość, plików nie ma rozszerzeń. Rozszerzenia w systemach Linux są zwykle nieistotne i nie są używane do definiowania typu pliku przez większość programów.
terdon
2
Zauważ, że twój pierwszy cytat jest źle cytowany, teraz wydaje się w zasadzie powiedzieć, że [!]jest podobny, ale nie taki[!]
ilkkachu
2
Warto wspomnieć, że ^ma dokładnie to samo znaczenie, co !w tym kontekście, dlatego [^a]wyklucza adokładnie tak, jak [!a]robi: Jeśli pierwszy znak następujący po [jest! lub ^, dopasowywany jest dowolny znak nie dołączony. ( man bash)
deser
3
rm myfile [!192]usunie myfile, a także każdy plik jednoznakowy 1, który nie ma nazwy 9, lub 2.
Kevin,

Odpowiedzi:

16

Powołując się 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 '-'.)

Nacisk na pojedynczej postaci części tutaj, []nie mogą być wykorzystywane do całych ciągów w bash Pattern Matching.


bash„s Brace rozszerzeń mogą być używane w celu dopasowania ciągów, jednak nie ma możliwości (wiem), aby je negować. Na przykład

1.{gif,csv,mp3}

jest rozwinięty do

1.gif 1.csv 1.mp3

bez względu na to, czy te pliki istnieją.


Jak wskazano wchargin , shoptmożna użyć operatorów rozszerzonego dopasowywania wzorców, z których jednym jest !(). Po uruchomieniu shopt -s extglobnp

1.!(gif|csv|mp3)

jest rozwinięty do

1.jpg 1.bmp 1.png

jeśli te pliki istnieją w bieżącym katalogu. Więcej informacji można znaleźć man bash(sekcja ROZSZERZENIE / Rozszerzenie nazwy ścieżki) i Rozszerzenie nazwy ścieżki (globbing) .


Do tego, co próbujesz zrobić, zawsze używałbym find, co oferuje negację i wiele więcej, np. W następujący sposób:

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

To tylko lista ustaleń, jeśli chcesz je usunąć, po prostu dołącz -deleteopcję na końcu polecenia. Jak zawsze przy usuwaniu czynności: zawsze najpierw sprawdź dokładnie .

findoferuje mnóstwo przydatnych opcji bardzo dobrze wyjaśnionych przez man find.

deser
źródło
1
Zauważ, że to findpolecenie jest rekurencyjne ( edytuj: jak pierwotnie pokazano - mogę zachować ten komentarz dla dodatkowych istotnych, ale nieistotnych informacji, które przekazuje). Aby znaleźć pliki znajdujące się tylko w bieżącym katalogu, ale nie także w jego podkatalogach - a zatem usuń te pliki tylko, jeśli -deleteakcja zostanie dodana - możesz dodać -maxdepth 1natychmiast po find .. Spowodowałoby to dostosowanie go do efektu globowania, ponieważ globsy nie są rekurencyjne w Bash, chyba że globstaropcja powłoki jest włączona i **używana. Glob pokazany w pytaniu nie jest rekurencyjny we wszystkich powłokach.
Eliah Kagan
2
„nie ma możliwości (wiem), aby je negować” -z shopt -s extglob, można użyć echo 1.!(gif|csv|mp3), która drukuje wszystko 1.ext, gdzie extnie jest gif, csvalbo mp3. (Zobacz podsekcję „Dopasowywanie wzorców” man bash.)
wchargin
10

Myślę, że źle zrozumiałeś użycie nawiasów. [abc]dopasowuje jeden z bohaterów a, blub c. [!abc]dopasowuje jeden znak, który nie jest a, blub c. Więc rozkaz

rm myfile [192]

usunie myfilei pliki 1, 9a 2ponieważ masz przestrzeń pomiędzy myfile i wspornika. W przeciwieństwie do polecenia

rm myfile[192]

usunie pliki myfile1, myfile9a myfile2.

pLumo
źródło
prawdziwe. Lepszy w użyciu find.
pLumo,
Właściwie [] jest używane dla zakresu, [!] Pojedynczej postaci
IronUhlan
1
@IronUhlan, oba mają zasięg lub listę znaków. Po prostu ten drugi jest zaprzeczeniem.
ilkkachu
7

Nawiasy [ ]oznaczają klasę postaci. Dowolny pojedynczy znak w nich może pasować na danej pozycji. Więc

file[192]

pasuje do trzech możliwych nazw:

file1
file2
file9

To nie nie pasuje file192, ponieważ dotyczy on tylko z jednego znaku po file.

Możemy również używać zakresów w nawiasach. [0-9]dopasowuje dowolną pojedynczą cyfrę na danej pozycji. Potrzebujemy dwóch cyfr [0-9][0-9]. W przypadku cyfry, po której następuje wielka litera, moglibyśmy użyć [0-9][A-Z]...

! wskazuje na negację.

file[!192]

dopasuje pliki, które mają dowolny pojedynczy znak po filewyjątkiem 1lub 9lub 2. Na przykład będzie pasować file7i filesa file%(i wiele innych), ale nie file192 , dlatego, że posiada dwa dodatkowe znaki.

W twoim przypadku sugeruję użycie findzamiast [!]. Ma notoperatora.

Jest również rekurencyjny , co oznacza, że ​​domyślnie przechodzi do wszystkich podkatalogów. Możesz ograniczyć go do bieżącego katalogu za pomocą -maxdepth 1, jeśli tego chcesz. Symbole wieloznaczne powłoki (globbing) nie są rekurencyjne (chociaż **można włączyć globalne rekurencyjne z ).

Zakładając, że nie chcesz unikać usuwania dowiązań symbolicznych, możemy dodać -type ddo zanegowanych testów, aby uniknąć usuwania katalogów. Dzięki Eliahowi Kaganowi za tę sugestię.

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

Jeśli zobaczysz, co chcesz, możesz ponownie uruchomić polecenie za pomocą -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete
Zanna
źródło
2
I myślę, że możesz zrobić kilka zakresów jednocześnie. Na przykład cyfra szesnastkowa może być[0-9a-fA-F]
Wilson
@Zanna, tak! Właściwie
używałem