`[!]` (dấu chấm than trong ngoặc) ký tự đại diện trong bash


10

Tôi đã bắt gặp các mẫu hình đại diện và ký tự đại diện và đặc biệt quan tâm đến tôi [!].

Cấu trúc này tương tự như [!]cấu trúc, ngoại trừ thay vì khớp bất kỳ ký tự nào trong ngoặc, nó sẽ khớp với bất kỳ ký tự nào, miễn là nó không được liệt kê giữa [].

rm myfile [!192]

Ở trên tôi tin rằng sẽ xóa bất kỳ / tất cả các tệp, ngoại trừ bất kỳ / tất cả các tệp có 192 trong tên của chúng.

Tuy nhiên, tôi lo ngại về việc sử dụng hợp lý phần này với phần mở rộng tệp và đặc biệt là nhiều điều kiện.

Điều gì sẽ là cú pháp thích hợp trong tình huống như vậy?

rm myfile [!.gif .csv. mp3] 

hoặc là

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

Lo lắng của tôi là khoảng thời gian có thể bị đặt sai vị trí, và vì vậy bất kỳ tệp nào.(chắc chắn là bất kỳ tệp nào trong số chúng?) Sau đó sẽ bị thao túng, khi tôi đang tìm cách gây ra thao tác với các tệp cụ thể.

Cấu trúc này tương tự như [ ]cấu trúc, ngoại trừ thay vì khớp bất kỳ ký tự nào trong ngoặc, nó sẽ khớp với bất kỳ ký tự nào, miễn là nó không được liệt kê giữa [].

(trích dẫn từ http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm )

Hiện nay; với tôi điều đó cho thấy một số ít !là đủ và tất cả các giá trị trong đó sau đó được chứa trong phạm vi.


5
như một mẹo chung, sử dụng ls my-patternhoặc echo my-commandđể kiểm tra xem việc tạo hình có được thực hiện theo cách bạn muốn hay không, trước khi chạyrm
Wayne_Yux

Ngoài ra, hãy nhớ rằng nhiều tệp, nếu không phải hầu hết, các tệp không có phần mở rộng. Phần mở rộng trên các hệ thống Linux thường không liên quan và không được sử dụng để xác định loại tệp theo phần lớn các chương trình.
terdon

2
Lưu ý rằng trích dẫn đầu tiên của bạn bị trích dẫn sai, bây giờ có vẻ như về cơ bản [!]là tương tự nhưng không giống như[!]
ilkkachu

2
Điều đáng nói là ^có ý nghĩa chính xác như !trong bối cảnh này, do đó [^a]loại trừ achính xác như [!a]: Nếu ký tự đầu tiên theo sau [là a! hoặc một ^ sau đó bất kỳ ký tự không kèm theo được khớp. ( man bash)
tráng miệng

3
rm myfile [!192]sẽ loại bỏ myfile, cũng như bất kỳ tập tin duy nhất nhân vật không có tên 1, 9hoặc 2.
Kevin

Câu trả lời:


16

Trích dẫn 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 '-'.)

Nhấn mạnh vào phần ký tự đơn ở đây, []không thể được sử dụng cho toàn bộ chuỗi trong Kết hợp mẫu Bash.


bash's Brace Expansion thể được sử dụng để phù hợp với chuỗi, tuy nhiên không có cách nào (tôi biết) để phủ nhận chúng. Ví dụ

1.{gif,csv,mp3}

được mở rộng đến

1.gif 1.csv 1.mp3

không có vấn đề cho dù các tập tin này tồn tại.


Như wchargein đã chỉ ra shoptcó thể được sử dụng để cho phép các toán tử khớp mẫu mở rộng, một trong số chúng là !(). Sau khi chạy shopt -s extglob, vd

1.!(gif|csv|mp3)

được mở rộng đến

1.jpg 1.bmp 1.png

nếu những tập tin đó tồn tại trong thư mục hiện tại Để đọc thêm man bash(phần MỞ RỘNG / Mở rộng tên đường dẫn) và mở rộng Tên đường dẫn (globalbing) .


Đối với những gì bạn đang cố gắng tôi luôn sử dụng find, điều này mang đến sự phủ định và hơn thế nữa, ví dụ như theo cách sau:

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

Điều này chỉ liệt kê các phát hiện, nếu bạn muốn loại bỏ chúng, chỉ cần thêm -deletetùy chọn vào cuối lệnh. Như mọi khi với các hành động loại bỏ: Luôn luôn kiểm tra cẩn thận đầu tiên .

findcung cấp một tấn các tùy chọn hữu ích được giải thích rất tốt bởi man find.


1
Lưu ý rằng findlệnh này là đệ quy ( chỉnh sửa: như ban đầu được hiển thị - Tôi có thể giữ bình luận này cho thông tin bổ sung nhưng không quan trọng mà nó truyền tải). Để tìm các tệp chỉ nằm trong thư mục hiện tại chứ không phải các thư mục con của nó - và do đó chỉ xóa các tệp đó nếu -deletehành động được thêm vào - bạn có thể thêm -maxdepth 1ngay sau đó find .. Điều này sẽ mang lại cho nó phù hợp với hiệu ứng của Globing, vì các khối u không có giá trị trong Bash trừ khi globstartùy chọn shell được bật và **được sử dụng. Quả cầu thể hiện trong câu hỏi là không hồi quy trong tất cả các vỏ.
Eliah Kagan

2
"không có cách nào (tôi biết) để phủ nhận họ là" -với shopt -s extglob, bạn có thể sử dụng echo 1.!(gif|csv|mp3), trong đó in tất cả các 1.extnơi extkhông phải là gif, csvhoặc mp3. (Xem phần phụ " man bash
Khớp

10

Tôi nghĩ rằng bạn đã hiểu nhầm việc sử dụng dấu ngoặc. [abc]phù hợp với một trong các nhân vật a, bhoặc c. [!abc]phù hợp với một nhân vật không a , bhoặc c. Vì vậy

rm myfile [192]

sẽ loại bỏ myfilevà các tập tin 1, 92bởi vì bạn có một không gian giữa myfile và khung. Ngược lại, lệnh

rm myfile[192]

sẽ loại bỏ các tập tin myfile1, myfile9myfile2.


thật. Tốt hơn để sử dụng find.
pLumo

Trên thực tế [] được sử dụng cho một phạm vi, [!] Một ký tự duy nhất
IronUhlan

1
@IronUhlan, cả hai đều có một phạm vi hoặc một danh sách các ký tự. Chỉ là khác là một phủ định.
ilkkachu

7

Chân đế [ ]biểu thị một lớp nhân vật. Bất kỳ nhân vật duy nhất bên trong họ có thể phù hợp ở vị trí nhất định. Vì thế

file[192]

khớp với ba tên có thể:

file1
file2
file9

không phù hợp file192, bởi vì nó chỉ giao dịch với một nhân vật duy nhất sau đó file.

Chúng tôi cũng có thể sử dụng phạm vi trong ngoặc. [0-9]phù hợp với bất kỳ chữ số duy nhất ở vị trí nhất định. Đối với hai chữ số, chúng ta cần [0-9][0-9]. Đối với một chữ số theo sau là chữ in hoa, chúng ta có thể sử dụng [0-9][A-Z]...

! chỉ ra sự phủ định.

file[!192]

sẽ phù hợp với các file mà có bất cứ ký tự đơn sau khi filetrừ 1hay 9hay 2. Ví dụ, nó sẽ khớp file7filesfile%(và nhiều người khác) nhưng không file192 , bởi vì điều đó có thêm hai ký tự.

Đối với trường hợp sử dụng của bạn, tôi đề nghị sử dụng findthay vì [!]. Nó có một notnhà điều hành.

Nó cũng được đệ quy , có nghĩa là nó đi vào tất cả các thư mục con theo mặc định. Bạn có thể giới hạn nó trong thư mục hiện tại với -maxdepth 1, nếu đó là những gì bạn muốn. Ký tự đại diện Shell (globalbing) là không đệ quy (mặc dù **có thể bật tính năng đệ quy đệ quy với ).

Giả sử bạn không muốn tránh xóa liên kết tượng trưng, ​​chúng tôi có thể thêm -type dvào các bài kiểm tra phủ định để tránh xóa bất kỳ thư mục nào. Cảm ơn Eliah Kagan cho đề nghị đó.

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

Nếu bạn thấy những gì bạn muốn, bạn có thể chạy lại lệnh với -delete

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

2
Và tôi nghĩ rằng bạn cũng có thể làm một vài phạm vi cùng một lúc. Ví dụ: chữ số thập lục phân có thể là[0-9a-fA-F]
Wilson

@Zanna, vâng! Tôi thực sự đang sử dụng find :) Không nhận ra nó có toán tử "không" :)
IronUhlan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.