`[!]` (tanda seru di kurung) wildcard di bash

10

Saya telah menemukan pola globbing dan wildcard dan yang menarik bagi saya adalah [!].

Konstruk ini mirip dengan [!]konstruk, kecuali daripada mencocokkan karakter apa pun di dalam tanda kurung, itu akan cocok dengan karakter apa pun, asalkan tidak terdaftar antara [dan ].

rm myfile [!192]

Di atas saya percaya akan menghapus semua file, kecuali untuk semua file yang memiliki 192 nama.

Namun saya khawatir tentang penggunaan yang tepat dari ini dengan ekstensi file, dan dalam beberapa kondisi tertentu.

Apa yang akan menjadi sintaksis yang tepat dalam situasi seperti itu?

rm myfile [!.gif .csv. mp3] 

atau

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

Kekhawatiran saya adalah bahwa periode mungkin salah tempat, dan file apa pun dengan .(yang akan menjadi salah satu dari mereka?) Kemudian akan dimanipulasi, ketika saya berusaha menyebabkan manipulasi file tertentu.

Konstruk ini mirip dengan [ ]konstruk, kecuali daripada mencocokkan karakter apa pun di dalam tanda kurung, itu akan cocok dengan karakter apa pun, asalkan tidak terdaftar antara [dan ].

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

Sekarang; bagi saya yang menyarankan maka singular !sudah cukup, dan semua nilai di dalamnya terkandung dalam kisaran.

IronUhlan
sumber
5
sebagai tip umum, gunakan ls my-patternatau echo my-commanduntuk memeriksa apakah globbing dilakukan seperti yang Anda inginkan, sebelum menjalankanrm
Wayne_Yux
Juga, ingatlah bahwa banyak, jika tidak sebagian besar, file tidak memiliki ekstensi. Ekstensi pada sistem Linux biasanya tidak relevan dan tidak digunakan untuk menentukan jenis file oleh sebagian besar program.
terdon
2
Perhatikan bahwa kutipan pertama Anda salah kutip, sekarang tampaknya pada dasarnya mengatakan bahwa [!]itu serupa tetapi tidak seperti[!]
ilkkachu
2
Patut disebutkan bahwa ^memiliki arti yang sama persis seperti !dalam konteks ini, karena itu [^a]mengecualikan apersis seperti [!a]: Jika karakter pertama yang mengikuti [adalah a! atau ^ maka setiap karakter yang tidak tertutup dicocokkan. ( man bash)
hidangan penutup
3
rm myfile [!192]akan menghapus myfile, serta setiap file tunggal-karakter tidak bernama 1, 9atau 2.
Kevin

Jawaban:

16

Mengutip 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 '-'.)

Penekanan pada bagian karakter tunggal di sini, []tidak dapat digunakan untuk seluruh string dalam Bash Pattern Matching.


bash's Brace Ekspansi dapat digunakan untuk mencocokkan string, namun tidak ada cara (saya tahu) untuk meniadakan mereka. Misalnya

1.{gif,csv,mp3}

diperluas ke

1.gif 1.csv 1.mp3

tidak masalah apakah file-file ini ada.


Seperti yang ditunjukkan oleh wcharginshopt dapat digunakan untuk memungkinkan operator pencocokan pola yang diperluas, salah satunya adalah !(). Setelah berlari shopt -s extglob, mis

1.!(gif|csv|mp3)

diperluas ke

1.jpg 1.bmp 1.png

jika file-file itu ada di direktori saat ini. Untuk lebih banyak membaca man bash(bagian EXPANSION / Perluasan Pathname) dan Perluasan Pathname (globbing) .


Untuk apa yang Anda coba lakukan, saya akan selalu menggunakan find, yang menawarkan negasi dan banyak lagi, misalnya dengan cara berikut:

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

Ini hanya mencantumkan temuan, jika Anda ingin menghapusnya, tambahkan saja -deleteopsi di akhir perintah. Seperti biasa dengan menghapus tindakan: Selalu periksa dengan cermat terlebih dahulu .

findmenawarkan banyak opsi berguna yang dijelaskan dengan sangat baik oleh man find.

pencuci mulut
sumber
1
Perhatikan bahwa findperintah ini bersifat rekursif ( sunting: seperti yang awalnya ditampilkan - saya mungkin menyimpan komentar ini untuk info tambahan yang relevan tetapi tidak penting yang disampaikannya). Untuk menemukan file yang berada hanya di direktori saat ini tetapi tidak juga subdirektori - dan dengan demikian hanya menghapus file-file itu jika -deletetindakan ditambahkan - Anda dapat menambahkan -maxdepth 1segera setelah find .. Ini akan membuatnya sejalan dengan efek globbing, karena gumpalan tidak recursive di Bash kecuali globstaropsi shell dihidupkan dan **digunakan. Glob yang ditunjukkan dalam pertanyaan ini tidak rekursif di semua shell.
Eliah Kagan
2
"tidak ada cara (saya tahu) untuk meniadakan mereka" -dengan shopt -s extglob, Anda dapat menggunakan echo 1.!(gif|csv|mp3), yang mencetak semua 1.extdi mana exttidak gif, csvatau mp3. (Lihat sub-bagian "Pencocokan Pola" dari man bash.)
wchargin
10

Saya pikir Anda salah memahami penggunaan tanda kurung. [abc]cocok dengan salah satu karakter a, batau c. [!abc]cocok dengan satu karakter yang tidak a , batau c. Jadi komandan

rm myfile [192]

akan menghapus myfiledan file 1, 9dan 2karena Anda memiliki ruang antara myfile dan braket. Sebaliknya, perintahnya

rm myfile[192]

akan menghapus file myfile1, myfile9dan myfile2.

pLumo
sumber
benar. Lebih baik digunakan find.
pLumo
Sebenarnya [] digunakan untuk rentang, [!] Satu karakter
IronUhlan
1
@IronUhlan, keduanya mengambil rentang atau daftar karakter. Hanya saja yang lain adalah negasi.
ilkkachu
7

Tanda kurung [ ]menunjukkan kelas karakter. Setiap karakter di dalamnya dapat cocok dengan posisi yang diberikan. Begitu

file[192]

cocok dengan tiga nama yang mungkin:

file1
file2
file9

Itu tidak cocok file192, karena hanya berurusan dengan karakter tunggal setelahnya file.

Kita juga bisa menggunakan rentang dalam kurung. [0-9]cocok dengan satu digit pada posisi yang diberikan. Untuk dua digit, kita perlu [0-9][0-9]. Untuk digit yang diikuti dengan huruf kapital, kita bisa menggunakan [0-9][A-Z]...

! menunjukkan negasi.

file[!192]

akan mencocokkan file yang memiliki karakter tunggal setelah filekecuali 1atau 9atau 2. Misalnya itu akan cocok file7dan filesdan file%(dan banyak lainnya) tetapi tidak file192 , karena itu memiliki dua karakter tambahan.

Untuk kasus penggunaan Anda, saya sarankan menggunakan findbukan [!]. Ia memiliki notoperator.

Itu juga rekursif , yang berarti masuk ke semua subdirektori secara default. Anda dapat membatasi ke direktori saat ini dengan -maxdepth 1, jika itu yang Anda inginkan. Shell wildcard (globbing) adalah non-rekursif (meskipun globbing rekursif dengan **dapat diaktifkan).

Dengan asumsi Anda tidak ingin menghindari menghapus symlink, kami dapat menambahkan -type dtes yang dinegasikan untuk menghindari menghapus direktori apa pun. Terima kasih kepada Eliah Kagan untuk saran itu.

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

Jika Anda melihat apa yang Anda inginkan, Anda dapat menjalankan perintah lagi dengan -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete
Zanna
sumber
2
Dan saya pikir Anda juga bisa melakukan beberapa rentang sekaligus. Misalnya, digit heksadesimal dapat berupa[0-9a-fA-F]
Wilson
@Zanna, ya! Saya benar-benar menggunakan find :) Tidak menyadari itu punya operator "tidak" :)
IronUhlan