Problema (s) de expresión regular en Bash: [^ negate] no parece funcionar

8

Cuando ejecuto ls /directory | grep '[^term]'en Bash obtengo una lista regular, como si el grepcomando se ignorara de alguna manera. Intenté lo mismo con egrep, intenté usarlo con comillas dobles y simples, pero no obtuve mejores resultados. Cuando lo intento ls /directory | grep '^[term], obtengo todas las entradas que comienzan con el término, como se esperaba.

He probado este comando en un editor en línea, donde puedo probar mi expresión regular y funcionó como debería. Pero no en Bash. Entonces funciona en una simulación, pero no en la vida real.

Trabajo en Crunchbang Linux 10. ¡Espero que sea suficiente información y espero con ansias cada pista, porque no ejecutar en un nivel tan básico y perder horas de tiempo es realmente frustrante!

erch
fuente
Estoy confundido por la negación en el título. ¿Quieres greplíneas que comienzan con el término. ¿O quieres buscar líneas que no contengan término?
Bernhard
@Bernhard: quiero una lista sin el término entre corchetes. ¡No tiene que ser exactamente "término"! Según tengo entendido, [^ abc] significa que cualquier cosa que contenga a, b o co o cualquier combinación de ella no debería estar en la lista.
erch

Respuestas:

12

¿Estás seguro de que lo que quieres está sucediendo? Cuando corres ls /directory | grep '[^term]', esencialmente estás buscando no las letras ter m. Esto significa que si un archivo tiene otras letras en su nombre, seguirá apareciendo en la salida de ls. Tome el siguiente directorio por ejemplo:

$ ls
alpha  brave  bravo  charlie  delta

Ahora si corro ls |grep '^[brav]'me sale lo siguiente:

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

Como puede ver, no solo obtuve bravey bravotambién obtuve alphaporque la clase de caracteres []obtendrá cualquier letra de esa lista.

En consecuencia, si ejecuto ls |grep '[^brav]'obtendré todos los archivos que no contienen los caracteres brav en ningún lugar del nombre.

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

Si observa que incluía la lista completa del directorio porque todos los archivos tenían al menos una letra que no estaba incluida en la clase de caracteres.

Entonces, como Kanvuanza dijo, para buscar el inverso del "término" en lugar de los caracteres t e r mque debes usar grep -v.

Por ejemplo:

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

Además, si no desea utilizar los archivos que tienen caracteres en la clase grep -v '[term]'. Eso evitará que aparezcan archivos que tengan alguno de esos caracteres. (Respuesta de Kanvuanza)

Por ejemplo:

$ ls |grep -v '[brav]'

Como puede ver, no había archivos listados porque todos los archivos en este directorio incluían al menos una letra de esa clase.

Apéndice:

Quería agregar que usando PCRE es posible usar solo regex para filtrar usando expresiones de negación. Para ello se usaría algo conocido como un negativo de expresiones regulares de preanálisis: (?!<regex>).

Entonces, usando el ejemplo anterior, puede hacer algo como esto para obtener los resultados que desea sin usar grepbanderas.

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

Para deconstruir esa expresión regular, primero coincide en el inicio de una línea ^y luego busca cadenas que no coincidan bravpara seguir después. Solo alpha, charliey deltacoinciden para que sean los únicos que se imprimen.

prateek61
fuente
1
Esto significa que si un archivo tiene otras letras en su nombre, seguirá apareciendo en la salida de ls. ¡Esto responde bastantes preguntas! :) Entonces, la mejor manera por el momento parece ser la -vopción. ¡Gracias por su apoyo! ¡Esta pregunta realmente arruinó mi tarde, donde tu respuesta ilumina mi noche!
erch
+1 para negative look-ahead regex.
Abhishek Kashyap
3

Supongo que esa grep -vbandera hace lo que quieres. Desde la página del manual :

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

Puede usar ls /directory | grep -v [term]para imprimir cualquier línea que no coincida.

Pedro Lacerda
fuente
Soy consciente de esta opción, pero ¿me equivoco al suponer que [^ xyz] es lo opuesto a [xyz] y que debería funcionar en cualquier caso? También quiero evitar editar cualquier configuración en cualquier lugar en un nivel tan básico. El uso de una opción de inversión y / o configuración de edición es una buena manera de evitarlo, pero por lo que yo entiendo, esto debería funcionar sin tener que salir de la caja.
erch
Supongo que tienes razón, esa es la notación común para la negación de clase (es decir. [^abc]Pero estoy bastante seguro de que grep no admite negaciones de clase, excepto algunas estándar (por ejemplo [[:^digits:]]). El apoyo de Grep para la negación es horrible !
Pedro Lacerda
¡El apoyo grep para la negación es horrible! Y estas son las pistas que son la verdadera guinda del pastel. Tengo los mismos problemas con egrep y estoy lejos de usar [al menos para mí aparentemente] comandos más avanzados en este momento. ¿Puedes sugerir un comando que proporcione mejores resultados y menos dolor de cabeza?
erch
@ cellar.dweller, grepel manejo de las clases de personajes está bien. Simplemente significa algo muy diferente de lo que (mal) entiendes. [abc]significa uno de a, bo c; [^abc]significa todo menos lo anterior. Es un personaje
vonbrand
@ cellar.dweller: Creo que su mayor problema es un malentendido de la expresión regular, específicamente las clases de caracteres dentro de la expresión regular.
tink