la répétition awk {n} ne fonctionne pas

18

J'essaie d'imprimer les lignes en utilisant le symbole de répétition {n} mais cela ne fonctionne pas. Pour. par exemple, je veux imprimer toutes les lignes dont la longueur est de 4 caractères

 awk '/^.{4}$/' test_data

Le code ci-dessus n'imprime pas cela. Comment le corriger pour que je puisse utiliser le symbole de répétition? Je connais l'alternative comme awk '/^....$/' test_dataetawk 'length ==3 ' test_data

Forever Learner
la source
3
Quelle distribution utilisez-vous? Quel awk?
terdon
1
$ awk --version GNU Awk 3.1.7 $ cat / etc / redhat-release Red Hat Enterprise Linux Server version 6.7 (Santiago)
Forever Learner
2
Je dirais awk '/^.{4}+$/{print}' <<<$'foods\nbaarsz\nfooo' correspondre exactement à 4 caractères. Aussi, comme vous l'avez mentionné vous-même, awk 'length($0) == 4' test_dataest compatible avec presque toutes les awkversions.
Valentin Bajrami
4
Faire awk --re-interval '/^.{4}$/' test_data ou awk --posix '/^.{4}$/' test_datatravailler?
steeldriver
Merci Steeldriver. Cela a résolu mon problème. A voté. Merci encore :)
Forever Learner

Réponses:

19

Selon le Guide de l'utilisateur de GNU Awk: historique des fonctionnalités , la prise en charge des opérateurs de plage d'expressions régulières a été ajoutée dans la version 3.0, mais l'option de ligne de commande explicite était initialement requise.

Nouvelles options de ligne de commande:

  • Nouvelles options de ligne de commande:
    • L'option --lint-old pour avertir des constructions qui ne sont pas disponibles dans la version Unix originale de awk (voir V7 / SVR3.1).
    • L'option -m de BWK awk. (Brian était toujours aux Laboratoires Bell à l'époque.) Cela a ensuite été retiré de son awk et de son gawk.
    • L'option --re-interval pour fournir des expressions d'intervalle dans les expressions régulières (voir Opérateurs d'expressions régulières).
    • L'option --traditional a été ajoutée comme meilleur nom pour --compat (voir Options).

Dans gawk4.0,

Les expressions d'intervalle font désormais partie des expressions régulières par défaut

Puisque vous utilisez gawk3.x, vous devrez utiliser

awk --re-interval '/^.{4}$/'

ou

awk --posix '/^.{4}$/'

ou (merci @ StéphaneChazelas) si vous voulez une solution portable, utilisez

POSIXLY_CORRECT=anything awk '/^.{4}$/'

(car --posixou --re-intervalprovoquerait une erreur dans d'autres awkimplémentations).

tournevis
la source
Merci Steeldriver, pour votre temps et votre aide. Surévalué et accepté comme réponse
Forever Learner
4
Il est préférable de l'utiliser POSIXLY_CORRECT=anything awk '/^.{4}/'car cela rend le code portable (un --posixou --re-intervalprovoquerait une erreur dans d'autres awkimplémentations).
Stéphane Chazelas
Salut Stéphane Chazelas, quand j'ai lancé la commande, $ POSIXLY_CORRECT = rien de awk '/^.{4}/' test_data, il a imprimé toutes les lignes. Puis j'ai réalisé qu'il n'y avait pas de dernier dollar après les répétitions. Merci pour vos contributions. Votez pour votre commentaire et votre solution. Désolé, je l'ai mal compris en premier lieu en raison de l'omission du $ après la répétition.
Forever Learner
20

Les ERE ( expressions régulières étendues utilisées par awkou egrep) n'avaient pas initialement {x,y}. Il a été introduit pour la première fois dans les BRE (tel qu'utilisé par grepou sed), mais avec une \{x,y\}syntaxe qui n'a pas brisé la portabilité en arrière.

Mais quand il a été ajouté aux ERE avec cette {x,y}syntaxe, il a rompu la portabilité en arrière car un foo{2}RE correspondait à quelque chose de différent auparavant.

Certaines implémentations ont donc choisi de ne pas le faire. Vous constaterez que /bin/awk, /bin/nawket /bin/egrepsur Solaris encore ne respecte pas (vous devez utiliser /usr/xpg4/bin/awkou /usr/xpg4/bin/grep -E). Idem pour awket nawksur FreeBSD (basé sur la version awkmaintenue par Brian Kernighan (l' kin awk)).

Pour GNUawk , jusqu'à relativement récemment (version 4.0), vous deviez l'appeler avec POSIXLY_CORRECT=anything awk '/^.{4}$/'pour l'honorer. mawkne l'honore toujours pas .

Notez que cet opérateur n'est que du sucre syntaxique. .{3,5}peut toujours être écrit ....?.?par exemple (bien que, bien sûr, il {3,5}soit beaucoup plus lisible et que l'équivalent (foo.{5,9}bar){123,456}soit bien pire).

Stéphane Chazelas
la source
Merci encore Stéphane Chazelas. Désolé, ma mauvaise, je n'ai pas pu comprendre votre réponse au départ. Merci beaucoup et voté positivement.
Forever Learner
6

Cela fonctionne comme prévu avec GNU awk(gawk):

$ printf 'abcd\nabc\nabcde\n' | gawk '/^.{4}$/'
abcd

Mais échoue avec mawkce qui est plus proche de POSIX awket, AFAIK, est la valeur par défaut sur les systèmes Ubuntu:

$ printf 'abcd\nabc\nabcde\n' | mawk '/^.{4}$/'
$ ## prints nothing

Ainsi, une solution simple serait d'utiliser à la gawkplace de awk. La {n}notation ne fait pas partie de la syntaxe POSIX BRE (expression régulière de base). C'est pourquoi grepéchoue également ici:

$ printf 'abcd\nabc\nabcde\n' | grep '^.{4}$'
$

Cependant, il fait partie des ERE (expressions régulières étendues):

$ printf 'abcd\nabc\nabcde\n' | grep -E '^.{4}$'
abcd

Je ne sais pas quelle saveur regex est utilisée par mawkou POSIX awk, mais je suppose que c'est BRE. Ils utilisent une ancienne version d'ERE selon la réponse de Stéphane . Dans tous les cas, soit vous utilisez apparemment une version awkqui n'implémente pas ERE ou votre entrée n'a en fait aucune ligne avec exactement 4 caractères. Cela peut se produire en raison d'espaces vides que vous ne voyez pas ou de glyphes unicode, par exemple.

terdon
la source
Salut terdon, je veux imprimer les lignes de 4 caractères. Pas les quatre premiers caractères d'une ligne. Par exemple, $ grep -E '^. {4} $' test_data, fonctionnera mais cela ne fonctionne pas avec awk
Forever Learner
@CppLearner oui, c'est ce que je fais ici. Que voulez-vous dire?
terdon
@CppLearner, la solution de @ terdon n'imprime que des lignes de 4 caractères. Mais si vous n'êtes vraiment intéressé que par la longueur de ligne, vous devez simplement utiliser length($0)ce qui est plus efficace que les expressions régulières.
Stephen Kitt
Salut terdon, la solution de steeldriver est ce que je cherchais. Merci pour votre temps. Bonjour Stephen Kitt, comme je l'ai mentionné dans le problème, j'ai déjà utilisé la longueur comme alternative, j'étais plus intéressé à savoir pourquoi la regex de répétition {n} ne fonctionnait pas à partir du commentaire de Steeldriver, j'ai appris que je devais utiliser l'option de --re-interval ou --posix. Merci pour votre temps.
Forever Learner
1
mawkn'est pas vraiment plus proche de POSIX awket n'utilise pas de BRE. Il utilise des ERE mais sans l' {x,y}opérateur.
Stéphane Chazelas