Masalah ekspresi reguler di Bash: [^ negate] tampaknya tidak berfungsi

8

Ketika saya mengeksekusi ls /directory | grep '[^term]'di Bash saya mendapatkan daftar reguler, seolah-olah grepperintahnya entah bagaimana diabaikan. Saya mencoba hal yang sama dengan egrep, saya mencoba menggunakannya dengan tanda kutip ganda dan tunggal, tetapi tidak ada hasil yang lebih baik. Ketika saya mencoba, ls /directory | grep '^[term]saya mendapatkan semua entri dimulai dengan istilah - seperti yang diharapkan.

Saya telah mencoba perintah ini di editor online, di mana saya dapat menguji regex saya dan itu berfungsi sebagaimana mestinya. Tapi tidak di Bash. Jadi itu berfungsi dalam simulasi, tetapi tidak dalam kehidupan nyata.

Saya bekerja di Crunchbang Linux 10. Saya harap ini adalah informasi yang cukup dan saya menantikan setiap petunjuk, karena gagal mengeksekusi pada tingkat dasar seperti itu dan membuang waktu berjam-jam benar-benar membuat frustrasi!

erch
sumber
Saya bingung karena meniadakan judul. Apakah Anda ingin grepbaris dimulai dengan istilah. Atau Anda ingin melihat baris yang tidak mengandung istilah sama sekali?
Bernhard
@Ernhard: Saya ingin daftar tanpa istilah dalam tanda kurung. Itu tidak harus persis 'istilah'! Sejauh yang saya mengerti, [^ abc] berarti segala sesuatu yang mengandung a, b atau c atau kombinasi apa pun tidak boleh ada dalam daftar.
erch

Jawaban:

12

Apakah Anda yakin apa yang Anda inginkan sedang terjadi? Ketika Anda menjalankan ls /directory | grep '[^term]'Anda pada dasarnya memahami untuk tidak ter huruf. Ini berarti jika suatu file memiliki huruf lain dalam namanya itu akan tetap muncul di output ls. Ambil direktori berikut sebagai contoh:

$ ls
alpha  brave  bravo  charlie  delta

Sekarang jika saya menjalankan ls |grep '^[brav]'saya mendapatkan yang berikut:

$ ls |grep '^[brav]'
alpha
brave
bravo

Seperti yang Anda lihat, saya tidak hanya mendapatkan bravedan bravosaya juga mendapat alphakarena kelas karakter []akan mendapatkan surat dari daftar itu.

Akibatnya, jika saya menjalankan ls |grep '[^brav]'saya akan mendapatkan semua file yang tidak mengandung karakter brav di mana saja dalam nama.

$ ls |grep '[^brav]'
alpha
bravo
brave
charlie
delta

Jika Anda perhatikan itu termasuk seluruh daftar direktori karena semua file memiliki setidaknya satu huruf yang tidak termasuk dalam kelas karakter.

Jadi seperti yang dikatakan Kanvuanza, untuk memahami kebalikan dari "istilah" yang bertentangan dengan karakter t e r mAnda harus melakukannya menggunakan grep -v.

Contohnya:

$ ls |grep -v 'brav'
alpha
charlie
delta

Juga jika Anda tidak ingin file yang memiliki karakter apa pun di kelas digunakan grep -v '[term]'. Itu akan membuat file tidak muncul yang memiliki karakter tersebut. (Jawaban Kanvuanza)

Contohnya:

$ ls |grep -v '[brav]'

Seperti yang Anda lihat tidak ada file yang terdaftar karena semua file dalam direktori ini termasuk setidaknya satu huruf dari kelas itu.

Tambahan:

Saya ingin menambahkan bahwa menggunakan PCRE dimungkinkan untuk menggunakan hanya regex untuk memfilter menggunakan ekspresi negate. Untuk melakukan ini, Anda akan menggunakan sesuatu yang dikenal sebagai negatif melihat-depan regex: (?!<regex>).

Jadi dengan menggunakan contoh di atas, Anda dapat melakukan sesuatu seperti ini untuk mendapatkan hasil yang Anda inginkan tanpa menggunakan grepflag.

$ ls | grep -P '^(?!brav)'
alpha
charlie
delta

Untuk mendekonstruksi regex itu, pertama-tama cocok pada awal baris ^dan kemudian mencari string yang tidak cocok bravuntuk diikuti sesudahnya. Hanya alpha,, charliedan deltacocokkan sehingga hanya itu yang dicetak.

prateek61
sumber
1
Ini berarti jika suatu file memiliki huruf-huruf lain dalam namanya, file itu akan tetap muncul di output ls. Ini menjawab beberapa pertanyaan! :) Jadi cara terbaik untuk saat ini sepertinya menjadi -vpilihan. Terima kasih atas dukunganmu! Pertanyaan ini benar-benar merusak sore saya, di mana jawaban Anda mencerahkan malam saya!
erch
+1 untuk negative look-ahead regex.
Abhishek Kashyap
3

Saya kira grep -vbendera itu melakukan apa yang Anda inginkan. Dari halaman manual :

-v, --invert-match
    Invert the sense of matching, to select non-matching lines.

Anda dapat menggunakan ls /directory | grep -v [term]untuk mencetak garis yang tidak cocok.

Pedro Lacerda
sumber
Saya mengetahui opsi ini, tetapi apakah saya salah dengan menganggap bahwa [^ xyz] adalah kebalikan dari [xyz] dan haruskah bekerja dalam hal apa pun? Saya juga ingin menghindari pengeditan pengaturan mana pun di tingkat dasar seperti ini. Menggunakan opsi pembalik dan / atau mengedit pengaturan tentu saja merupakan cara yang baik, tetapi sejauh yang saya mengerti, ini harus bekerja tanpa, di luar kotak.
erch
Saya kira Anda benar, itu adalah notasi umum untuk negasi kelas (mis. [^abc]Tapi saya cukup yakin bahwa grep tidak mendukung negasi kelas, kecuali beberapa yang standar (mis. [[:^digits:]]). Dukungan grep untuk negasi mengerikan !
Pedro Lacerda
Dukungan grep untuk negasi sangat buruk! Dan ini adalah petunjuk bahwa icing nyata pada kue. Saya memiliki masalah yang sama dengan egrep dan saya jauh dari menggunakan [setidaknya bagi saya tampaknya] perintah yang lebih maju saat ini. Bisakah Anda menyarankan perintah yang memberikan hasil yang lebih baik dan mengurangi sakit kepala?
erch
@ cellar.dweller, greppenanganan kelas karakter tidak masalah. Itu hanya berarti sesuatu yang sangat berbeda dari apa yang Anda (salah) mengerti. [abc]berarti salah a, batau c; [^abc]berarti apa pun kecuali di atas. Itu adalah satu karakter.
vonbrand
@ cellar.dweller: Saya pikir masalah terbesar Anda adalah kesalahpahaman regex, khususnya kelas karakter dalam regex.
tink