Pourquoi l'ancre $ de fin de ligne ne fonctionne-t-elle pas avec la commande grep, même si l'ancre ^ de début de ligne l'est?

19

Très nouveau pour UNIX mais pas nouveau pour la programmation. Utilisation de Terminal sur MacBook. Dans le but de gérer et de rechercher des listes de mots pour la construction de mots croisés, j'essaie de me familiariser avec la commande Grep et ses variantes. Cela semble assez simple, mais se bloquer dès le début avec ce que je pensais être un cas simple.

Quand j'entre

grep "^COW" masternospaces.txt

J'obtiens ce que je veux: une liste de tous les mots commençant par COW.

Mais quand j'entre

grep "COW$" masternospaces.txt

Je m'attends à obtenir une liste de mots se terminant par COW (il y en a beaucoup), et rien n'est retourné du tout.

Le fichier est un fichier texte brut, avec chaque ligne juste un mot (ou une expression de mot sans espaces) dans toutes les majuscules.

Une idée de ce qui pourrait se passer ici?

DTalvacchio
la source
3
Quelle est l'origine du fichier masternospaces.txt? est-il possible qu'il ait des terminaisons de ligne de style Windows (CR-LF) au lieu de LF de style Unix?
steeldriver
2
Pas sûr, mais cherchez-vous une liste de mots ou une liste de lignes ... ?
mikeserv
steeldriver-- Quelque chose comme ça a été ma première pensée. Je ne savais pas comment inspecter ce qui s'y passait, ni quelles étaient les possibilités. Supposait qu'un retour final était un retour final. Ce fichier est un vaste recueil provenant de quelques sources. Je ne sais même pas lequel serait considéré comme le fichier d'origine. Et c'est passé par au moins trois traitements de texte sur les machines PC et Mac. Quelle pourrait être la meilleure façon de voir quel type de terminaisons il utilise?
DTalvacchio
mikeserv - Dans ce fichier .txt, chaque ligne est juste un mot (ou une phrase sans espace entre les mots, donc encore un "mot"). Je recherche donc des lignes, je suppose. . . juste que chaque ligne n'a qu'un seul de ce que je considère comme un mot pour les mots croisés.
DTalvacchio
1
Vous pouvez utiliser hexdumppour vérifier exactement comment vos fins de ligne sont formatées. Je vous suggère d' utiliser le format mon préféré: hexdump -e '"%08_ad (0x%08_ax) "8/1 "%02x "" "8/1 "%02x "' -e '" "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt. Avec la sortie, vérifiez les fins de ligne: 0a-> LF, 0d-> CR.
user43791

Réponses:

23

Comme l'a mentionné @steeldriver, le problème est probablement dû à un style de fin de ligne différent de celui grepattendu.

Pour vérifier les fins de ligne

Vous pouvez utiliser hexdumppour vérifier exactement comment vos fins de ligne sont formatées. Je vous suggère d'utiliser mon format préféré:

hexdump -e '"%08_ad (0x%08_ax)    "8/1 "%02x ""   "8/1 "%02x "' -e '"    "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt

Avec la sortie, vérifiez les fins de ligne: 0a-> LF, 0d-> CR. Un exemple très rapide donnerait quelque chose comme ceci:

$ hexdump -e '"%08_ad (0x%08_ax)    "8/1 "%02x ""   "8/1 "%02x "' -e '"    "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt
00000000 (0x00000000)    4e 6f 20 43 4f 57 20 65   6e 64 69 6e 67 0d 0a 45    No COW e|nding..E
00000016 (0x00000010)    6e 64 69 6e 67 20 69 6e   20 43 4f 57 0d 0a          nding in| COW..

Notez les fins de ligne au format dos: 0d 0a.

Pour modifier les fins de ligne

Vous pouvez voir ici ou ici pour différentes méthodes de modification des fins de ligne à l'aide de divers outils, mais pour une chose ponctuelle, vous pouvez toujours utiliser vi / vim:

vim masternospaces.txt
:set fileformat=unix
:wq

Grep sans rien changer

Si vous voulez juste grepfaire correspondre quelque soit la fin de ligne, vous pouvez toujours spécifier des fins de ligne comme ceci:

grep 'COW[[:cntrl:]]*$' masternospaces.txt

Si une ligne vierge s'affiche, vous pouvez vérifier que vous avez bien correspondu à quelque chose en utilisant l' -voption de cat:

grep 'COW[[:cntrl:]]*$' masternospaces.txt | cat -v

Mon préféré

Vous pouvez également à la fois grep et standardiser la sortie en utilisant sed:

sed -n '/COW^M*$/{;s/^M//g;p;};' masternospaces.txt

^Mest obtenu en tapant Ctrl-V Ctrl-Msur votre clavier.

J'espère que cela t'aides!

user43791
la source
Tout cela est extrêmement utile. Je n'ai plus de temps aujourd'hui, mais je regarderai attentivement tout cela demain et je verrai quoi. Si entre-temps l'un de vous a un lien vers votre guide de référence de commande Unix préféré afin que je puisse m'apprendre un peu comment les choses fonctionnent, j'apprécierais. J'ai ramassé des morceaux ici et là mais je n'ai pas encore trouvé une source qui soit mon go-to pour des explications. Merci à tous et reviendrons demain avec une mise à jour, espérons-le, réussie. --D
DTalvacchio
C'est dommage que ce post n'ait pas de fermeture, du moins pour moi. Je ne peux pas, pour la vie de moi, comprendre comment faire correspondre la fin de la ligne. Si je fais un vidage hexadécimal, je ne trouve pas une belle ligne se terminant comme votre exemple ci-dessus. Je ne suis pas familier avec le hexagone, donc je ne le lis peut-être pas correctement. J'ai également essayé le [[:cntrl:]]@ user43791 suggéré et il ne correspond toujours à rien pour moi. Cela n'a aucun sens. J'utilise GNU grep 2.20 et analyse la sortie de nDPI qui a été écrite dans un fichier texte
harperville
@harperville Si vous cat -v yourfile.ext, que voyez-vous?
user43791
Eh bien, rien d'excitant ou d'inattendu. Juste le contenu comme je m'attendrais à les voir. Quelque chose de spécifique que vous recherchez? Je ne peux pas coller la sortie ici mais je vois juste le contenu. Régulier «texte anglais ASCII» selon file.
harperville
@harperville Pas de "^ M" supplémentaire à la fin de chaque ligne? Pourriez-vous coller les premières lignes hexadécimales?
user43791
1

Bien que vous puissiez utiliser la syntaxe RegEx «standard» avec grep (comme dans la réponse de @ user43791 ), grep a également d'autres identifiants pour signifier les limites d'entrée.

Les matchers pour le début et la fin de la ligne entière sont \`(backtick) (au lieu de ^) et \'(apostrophe) (au lieu de $).

Donc, pour votre commande d'origine, vous utiliseriez: grep "COW\'" masternospaces.txt

Note latérale: Il est également important de noter cela ?et +sera traité littéralement à moins que vous ne leur échappiez en utilisant \?et \+en faire leurs homologues de sélection de style RegEx.

Source: grepsyntaxe des expressions régulières

samthecodingman
la source
grep prend ^ (caret) pour le début et \ '(apostrophe) pour la fin
GypsyCosmonaut
1

Une autre façon de supprimer l' \ravant le grep:

... | dos2unix | egrep 'COW$' | ...

J'aime que c'est très clair car je ne me souviens pas de choses comme [[:cntrl:]]longtemps.

Javier
la source
-2

"COW $" lorsque bash a défini le paramètre pour grep, il a été interprété comme "COW", où "$" est traité comme "", car $ est un simbole d'échappement. quand rien n'a été copié par $, il est interprété comme une chaîne vide par le shell bash, vous devriez donc utiliser grep 'COW $' masternospaces.txt à la place.

yangyang
la source
3
puisqu'il n'y a pas d'expansion valide de $, il serait laissé seul par bash et utilisé par grep. Voyez par vous-même: echo "COW$"- la $volonté sera toujours là.
Jeff Schaller
-3

Dans BSD grep, vous devez échapper "$" et mettre votre chaîne entre guillemets:

"COW\$"
user297403
la source
1
Um non. Le $ne sera pas spécial pour le shell, car le truc qui le suit n'est pas un nom de variable de shell valide. L'utilisation de guillemets simples autour de chaînes statiques est une meilleure idée, mais ne fera aucune différence ici.
Kusalananda