Dis que j'ai des lignes comme ceci:
*[234]*
*[23]*
*[1453]*
où *
représente une chaîne (sauf une chaîne de formulaire [number]
). Comment puis-je analyser ces lignes avec une utilitaire de ligne de commande et extraire le nombre entre crochets?
Plus généralement, lequel de ces outils cut
, sed
, grep
ou awk
serait approprié pour une telle tâche?
Si vous avez GNU Grep, vous pouvez utiliser son -o
Option de recherche d'une regex et de sortie uniquement la partie correspondante. (D'autres implémentations GREP ne peuvent montrer que la ligne entière.) S'il existe plusieurs correspondances sur une ligne, elles sont imprimées sur des lignes distinctes.
grep -o '\[[0-9]*\]'
Si vous ne voulez que les chiffres et non les crochets, c'est un peu plus difficile; Vous devez utiliser une affirmation de largeur zéro: une regexp qui correspond à la chaîne vide, mais uniquement si elle est précédée ou suivie, selon le cas, par un support. Les assertions de largeur zéro ne sont disponibles que dans la syntaxe Perl.
grep -P -o '(?<=\[)[0-9]*(?=\])'
Avec SED, vous devez éteindre l'impression avec -n
, et faire correspondre toute la ligne et ne conserve que la partie correspondante. S'il y a plusieurs matchs possibles sur une ligne, seul le dernier match est imprimé. Voir extraire une regex assortie avec 'SED' sans imprimer les caractères environnants pour plus de détails sur l'utilisation de SED ici.
sed -n 's/^.*\(\[[0-9]*\]\).*/\1/p'
ou si vous voulez seulement les chiffres et non les crochets:
sed -n 's/^.*\[\([0-9]*\)\].*/\1/p'
Sans grep -o
, Perl est l'outil de choix ici si vous voulez quelque chose qui est à la fois simple et compréhensible. Sur chaque ligne (-n
), si la ligne contient une correspondance pour \[[0-9]*\]
, puis imprimez ce match ($&
) et une nouvelle ligne (-l
).
Perl -l -ne '/\[[0-9]*\]/ and print $&'
Si vous ne voulez que les chiffres, mettez des parenthèses dans la regex pour délimiter un groupe et imprimer uniquement ce groupe.
Perl -l -ne '/\[([0-9]*)\]/ and print $1'
P.s. Si vous souhaitez uniquement avoir besoin d'un ou plusieurs chiffres entre les crochets, changez [0-9]*
à [0-9][0-9]*
, ou pour [0-9]+
à Perl.
Vous ne pouvez pas le faire avec cut
.
tr -c -d '0123456789\012'
sed 's/[^0-9]*//g'
awk -F'[^0-9]+' '{ print $1$2$3 }'
grep -o -E '[0-9]+'
tr
est l'ajustement le plus naturel pour le problème et dirigerait probablement le plus rapide, mais je pense que vous auriez besoin d'intrants gigantesques pour séparer l'une de ces options en termes de vitesse.
Si vous voulez dire extraire un ensemble de chiffres consécutifs entre les caractères non chiffres, je suppose que sed
et awk
sont les meilleurs (bien que grep
est également capable de vous donner les caractères correspondants ):
sed
: vous pouvez bien sûr correspondre aux chiffres, mais il est peut-être intéressant de faire le contraire, supprimer les non-chiffres (fonctionne aussi loin qu'il n'y a qu'un numéro par ligne):
$ echo nn3334nn | sed -e 's/[^[[:digit:]]]*//g'
3344
grep
: vous pouvez correspondre à des chiffres consécutifs
$ echo nn3334nn | grep -o '[[:digit:]]*'
3344
Je ne donne pas d'exemple pour awk
parce que j'ai une expérience nulle avec elle; Il est intéressant de noter que, bien que sed
est un couteau suisse, grep
vous donne un moyen plus simple et plus lisible de le faire, ce qui fonctionne également pour plus d'un numéro sur chaque ligne d'entrée ( les -o
n'imprime que les parties correspondantes de l'entrée, chacune sur sa propre ligne):
$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54
Comme il a été dit que cela ne peut être fait avec cut
, je vais montrer qu'il est facilement possible de produire une solution au moins pire que certaines des autres, même si je n'approuve pas l'utilisation de cut
comme la "meilleure" solution (ou même une solution particulièrement bonne). Il faut dire que toute solution à la recherche ne sont pas spécifiquement pour *[
et ]*
autour des chiffres facilite la simplification des hypothèses et est donc sujette à une défaillance des exemples plus complexes que celle donnée par l'Asker (par exemple des chiffres extérieurs *[
et ]*
, qui ne devrait pas être montré). Cette solution vérifie au moins pour les crochets et pourrait être étendue pour vérifier les astérisques également (à gauche comme exercice au lecteur):
cut -f 2 -d '[' myfile.txt | cut -f 1 -d ']'
Cela utilise le -d
Option, qui spécifie un delimiter. Évidemment, vous pouvez également faire du tuyau dans l'expression cut
au lieu de lire à partir d'un fichier. Tandis que cut
est probablement assez rapide, car il est simple (pas de moteur de regex), vous devez l'invoquer au moins deux fois (ou quelques fois de plus pour vérifier *
), qui crée un peu de processus. Le seul avantage réel de cette solution est qu'il est plutôt lisible, en particulier pour les utilisateurs occasionnels non versés dans les constructions de regex.