Problème (s) d'expression régulière dans Bash: [^ negate] ne semble pas fonctionner

8

Lorsque j'exécute ls /directory | grep '[^term]'dans Bash, j'obtiens une liste régulière, comme si la grepcommande était ignorée d'une manière ou d'une autre. J'ai essayé la même chose avec egrep, j'ai essayé de l'utiliser avec des guillemets doubles et simples, mais sans meilleurs résultats. Lorsque j'essaie, ls /directory | grep '^[term]j'obtiens toutes les entrées commençant par le terme - comme prévu.

J'ai essayé cette commande dans un éditeur en ligne, où je peux tester mon expression régulière et cela a fonctionné comme il se doit. Mais pas dans Bash. Cela fonctionne donc dans une simulation, mais pas dans la vraie vie.

Je travaille sur Crunchbang Linux 10. J'espère que ces informations sont suffisantes et j'attends avec impatience chaque indice, car ne pas exécuter à un niveau aussi basique et perdre des heures est vraiment frustrant!

erch
la source
Je suis confus à cause du négatif dans le titre. Voulez-vous des greplignes commençant par un terme. Ou voulez-vous chercher des lignes ne contenant pas du tout de terme?
Bernhard
@Bernhard: Je veux une liste sans le terme entre crochets. Il n'est pas nécessaire que ce soit exactement le terme! Pour autant que je l'ai compris, [^ abc] signifie que tout ce qui contient a, b ou c ou toute combinaison de ceux-ci ne devrait pas être dans la liste.
erch

Réponses:

12

Êtes-vous sûr que ce que vous voulez se passe? Lorsque vous courez, ls /directory | grep '[^term]'vous ne cherchez pas essentiellement les lettres ter m. Cela signifie que si un fichier a d'autres lettres dans son nom, il apparaîtra toujours dans la sortie de ls. Prenez par exemple le répertoire suivant:

$ ls
alpha  brave  bravo  charlie  delta

Maintenant, si je cours, ls |grep '^[brav]'j'obtiens ce qui suit:

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

Comme vous pouvez le voir, non seulement j'ai reçu braveet bravoj'ai aussi obtenu alphaparce que la classe de personnage []recevra n'importe quelle lettre de cette liste.

Par conséquent, si je lance ls |grep '[^brav]', j'obtiendrai tous les fichiers qui ne contiennent pas les caractères brav n'importe où dans le nom.

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

Si vous remarquez qu'il comprenait la liste complète des répertoires car tous les fichiers avaient au moins une lettre qui n'était pas incluse dans la classe de caractères.

Donc, comme Kanvuanza l'a dit, pour rechercher l'inverse de "terme" par opposition aux caractères, t e r mvous devez le faire en utilisant grep -v.

Par exemple:

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

Aussi, si vous ne voulez pas que les fichiers contenant des caractères dans la classe soient utilisés grep -v '[term]'. Cela empêchera l'apparition de tous les fichiers contenant l'un de ces caractères. (Réponse de Kanvuanza)

Par exemple:

$ ls |grep -v '[brav]'

Comme vous pouvez le voir, aucun fichier n'était répertorié car tous les fichiers de ce répertoire comprenaient au moins une lettre de cette classe.

Addenda:

Je voulais ajouter qu'en utilisant PCRE, il est possible d'utiliser simplement des expressions rationnelles pour filtrer en utilisant des expressions négatives. Pour ce faire , vous utilisez quelque chose de connu comme anticipation négative regex: (?!<regex>).

Donc, en utilisant l'exemple ci-dessus, vous pouvez faire quelque chose comme ça pour obtenir les résultats souhaités sans utiliser de grepdrapeaux.

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

Pour déconstruire cette expression régulière, elle correspond d'abord au début d'une ligne ^, puis recherche les chaînes qui ne correspondent pas bravà suivre ensuite. Seulement alpha, charlieet deltacorrespondent donc ce sont les seuls qui sont imprimés.

prateek61
la source
1
Cela signifie que si un fichier a d'autres lettres dans son nom, il apparaîtra toujours dans la sortie de ls. Cela répond à plusieurs questions! :) Donc, la meilleure façon pour le moment semble être l' -voption. Merci pour votre aide! Cette question a vraiment gâché mon après-midi, où votre réponse égaye ma soirée!
erch
+1 pour negative look-ahead regex.
Abhishek Kashyap
3

Je suppose que ce grep -vdrapeau fait ce que vous voulez. Depuis la page de manuel :

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

Vous pouvez utiliser ls /directory | grep -v [term]pour imprimer toutes les lignes qui ne correspondent pas.

Pedro Lacerda
la source
Je connais cette option, mais ai-je tort de supposer que [^ xyz] est l'opposé de [xyz] et devrait fonctionner dans tous les cas? Je veux également éviter de modifier des paramètres n'importe où à un niveau aussi basique. L'utilisation d'une option d'inversion et / ou de la modification des paramètres est certainement un bon moyen de contourner, mais pour autant que je l'ai compris, cela devrait fonctionner sans, hors de la boîte.
erch
Je suppose que vous avez raison, c'est la notation commune pour la négation de classe (c'est-à-dire. [^abc]Mais je suis à peu près sûr que grep ne prend pas en charge les négations de classe, sauf quelques-unes standard (par exemple. [[:^digits:]]). Le support de grep pour la négation est horrible !
Pedro Lacerda
Le soutien de Grep à la négation est horrible! Et ce sont les indices qui sont la vraie cerise sur le gâteau. J'ai les mêmes problèmes avec egrep et je suis loin d'utiliser [au moins pour moi apparemment] des commandes plus avancées pour le moment. Pouvez-vous suggérer une commande qui fournit de meilleurs résultats et moins de maux de tête?
erch
@ cellar.dweller, grepla gestion des classes de caractères est très bien. Cela signifie simplement quelque chose de très différent de ce que vous (mal) comprenez. [abc]des moyens de une a, bou c; [^abc]signifie tout sauf ce qui précède. C'est un personnage.
vonbrand
@ cellar.dweller: Je pense que votre plus gros problème est une mauvaise compréhension de l'expression régulière, en particulier des classes de caractères au sein de l'expression régulière.
tink