commande grep pour afficher toutes les lignes qui commencent et se terminent par le même caractère


8

Je veux savoir comment utiliser greppour afficher toutes les lignes qui commencent et se terminent par le même caractère.

Réponses:


14

POSIX:

pattern='\(.\).*\1
.'
grep -x -- "$pattern" file

Cela ne fonctionnera pas si la ligne commence ou se termine avec un caractère d'octet non valide, si vous voulez couvrir ce cas, vous pouvez l'ajouter LC_ALL=C, bien que cela ne LC_ALL=Cfonctionne qu'avec des données de caractère à un seul octet.


perl6 semble être le meilleur outil, si vous l'avez dans votre boîte:

$ printf '\ue7\u301 blah \u107\u327\n121\n1\n123\n' |
  perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'
ḉ blah ḉ
121
1

Bien qu'il s'étouffe toujours avec des caractères invalides.


Notez que perl6cela modifiera votre texte en le transformant en NFCforme:

$ printf '\u0044\u0323\u0307\n' |
  perl6 -pe ''                  |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+1e0c
U+0307
U+000a

$ printf '\u0044\u0323\u0307\n' |
  perl -pe ''                   |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+0044
U+0323
U+0307
U+000a

En interne, perl6stocke la chaîne sous NFGforme (stand for Normalization Form Grapheme), qui est un perl6moyen inventé de traiter correctement les graphèmes non précomposés:

$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.chars.say'
1
$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.codes.say'
2

2
La gestion par Perl du texte Unicode n'est rien de moins qu'exemplaire, au point que de nombreuses tâches "simples" en Perl sont pratiquement impossibles à implémenter à l'aide d'autres outils, au moins avec le même niveau d'exactitude.
Dietrich Epp

1
Il convient de noter que perl6cela modifiera le texte (le transformer en NFC (formulaire de normalisation "composé")).
Stéphane Chazelas

@ StéphaneChazelas: Oui, juste point. Notez également que la chaîne dans perl6est stockée sous NFGforme ( Gpour Grapheme), ce qui est un perl6moyen de gérer correctement les graphèmes non pré-composés.
cuonglm

10

Pas grep mais awk:

awk -F "" 'NF && $1 == $NF'

Ces cas particuliers sont traités:

  • il n'imprime pas les lignes vides
  • il imprime toujours des lignes à 1 caractère

Un FS vide divise l'enregistrement en un caractère par champ dans gawk, mawket busybox awk(octets, pas des caractères pour les deux derniers), mais n'est pas standard et ne fonctionne pas dans les implémentations de awkdérivé de l'original par A, W et K comme sur les BSD et les Unités commerciales. Plus portable mais plus à taper:

awk '/./ && substr($0,1,1) == substr($0,length)'

1
Notez que la FSchaîne vide n'est pas standard et ne fonctionnera pas dans certaines awkimplémentations.
cuonglm

2
Alternative qui évite le fractionnement et est entièrement portable (même pour le "vieux" awk Solaris au maximum) awk 'length&&substr($0,1,1)==substr($0,length)'(notez l'argument par défaut de lengthis $0et l'action par défaut est {print $0})
dave_thompson_085

@ dave_thompson_085: thx, je n'utilise que votre indice d'action par défaut pour avoir la commande la plus courte.
rudimeier

Firne. Une correction mineure; mon test pour Solaris old awk s'est trompé (j'ai accidentellement eu xpg4 sur) mais cette méthode fonctionne, nawkce qui est presque aussi mauvais :-)
dave_thompson_085

8
grep -xe '\(.\).*\1' -e .

Exemple:

$ printf '%s\n' il y était cet été  | grep -xe '\(.\).*\1' -e .
y
été

-xest pour une correspondance exacte (correspondance sur toute la ligne). \1étant une référence arrière au personnage capturé dans \(.\). Nous ajoutons un -e .pour prendre en charge le cas particulier d'une ligne contenant un seul caractère.

Il suppose que l'entrée contient du texte valide dans les paramètres régionaux actuels.

La correspondance est sur le caractère , pas sur l' octet (ceux é en UTF-8 sont les deux octets 0xc3 0xa9 par exemple), ni le graphe graphem (cela ne fonctionnerait pas si ceux é étaient écrits sous leur forme décomposée avec esuivi du U + 0301 combinant un accent aigu par exemple).

Pour travailler sur des grappes graphem, avec un grepqui prend en charge -PPCRE:

$ printf 'e\u0301te\u0301\n' | grep -xPe '(\X).*\1|\X'
été

Cela suppose que la décomposition est la même pour les deux grappes, par exemple un exprimé comme c U+0301 U+0327ne correspondrait pas à un exprimé comme c U+0327 U+0301ou ć( U+0107) U+0327ou ç( U+00E7) U+0301ou ḉ ( U+1E09). Pour cela, vous devez effectuer la vérification sur un formulaire normalisé:

$ printf '\ue7\u301 blah \u107\u327\n' |
  perl -MUnicode::Normalize -C -ne '
    print if /^\X$/ || NFC($_) =~ /^(\X).*\1$/'
ḉ blah ḉ

1
Si vous en avez perl6, alors perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'faites tout le travail pour vous.
cuonglm

1

Alternative rapide à python2:

python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt

Exemple:

$ python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt  | cat -A 
nathan$
 ookie $
a line a$

Il échoue si la ligne contient des espaces de fin ou de début, par exemple «121».
cuonglm

@cuonglm c'est vrai. Mais est-ce que les espaces de fuite ou de tête étaient une exigence? Cela fait le travail demandé - vérifiez si le premier et le dernier caractère sont identiques. L'espace est toujours un caractère ascii, non?
Sergiy Kolodyazhnyy

Soit dit en passant, @cuonglm a échoué avec l'espace de fuite et de tête :)
Sergiy Kolodyazhnyy

Votre code supprime les espaces blancs de début et de fin, il modifie donc la ligne d'entrée. Cela donne également une erreur pour les lignes vides.
rudimeier

@Serg: Comment? ma réponse ne fait que saluer, elle ne modifie pas l'entrée.
cuonglm
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.