2010-05-03 7 views
6

Je l'ai cherché un peu mais je dois utiliser les mauvais termes - est-ce que ruby ​​a un moyen de grep pour une chaîne/regex et retourne aussi les 5 lignes environnantes (ci-dessus et ci-dessous)? Je sais que je pourrais juste appeler "grep -C 5 ..." ou même écrire ma propre méthode, mais il semble que quelque chose aurait ruby ​​et je n'utilise pas les bons termes de recherche.Ruby équivalent à "grep -C 5" pour obtenir le contexte des lignes autour du match?

Répondre

6

Vous pouvez le faire avec une expression régulière. Voici la chaîne que nous voulons rechercher:

s = %{The first line 
The second line 
The third line 
The fourth line 
The fifth line 
The sixth line 
The seventh line 
The eight line 
The ninth line 
The tenth line 
} 

EOL est « \ n » pour moi, mais pour vous, il est peut-être « \ r \ n ». Je vais le coller dans une constante:

EOL = '\n' 

Pour simplifier l'expression régulière, nous allons définir le modèle pour « contexte » juste une fois:

CONTEXT_LINES = 2 
CONTEXT = "((?:.*#{EOL}){#{CONTEXT_LINES}})" 

Et nous allons rechercher pour toute ligne contenant le mot "cinquième". Notez que cette expression régulière doit saisir toute la ligne, y compris la fin de ligne, pour que cela fonctionne:

regexp = /.*fifth.*#{EOL}/ 

Enfin, faire la recherche et afficher les résultats:

s =~ /^#{CONTEXT}(#{regexp})#{CONTEXT}/ 
before, match, after = $1, $2, $3 
p before # => "The third line\nThe fourth line\n" 
p match  # => "The fifth line\n" 
p after  # => "The sixth line\nThe seventh line\n" 
+0

C'était super, merci! Cela fonctionne comme un charme - je devais revenir en arrière et relire que la fin de ligne doit être incluse dans l'expression régulière que je cherche, j'ai manqué cela quand j'ai d'abord essayé ceci. – wonderfulthunk

+0

Cela devrait fonctionner: /.*fifth.*[#{EOL}]*/ Voir http://rubular.com/r/skalXLBXcQ –

0

Je ne pense pas que vous pouvez fournir des args pour grep; basé sur le api.

Vous pouvez toujours écrire une méthode. Quelque chose dans le sens de ceci:

def new_grep(enum, pattern, lines) 
values = enum.grep(/pattern/).map do |x| 
    index = enum.index(x) 
    i = (index - lines < 0) ? 0 : index - lines 
    j = (index + lines >= enum.length) ? enum.length-1 : index + lines 
    enum[i..j] 
end 
return values.flatten.uniq 
end 
+0

Celui-ci pourrait aussi fonctionner, mais j'ai trouvé la réponse de Wayne plus conforme à ce que je voulais. – wonderfulthunk

2

Merci pour le grep contextuel. Je pensais que je pourrais ajouter, que lorsque le match se rapproche du haut ou du bas et que vous voulez encore toutes les lignes peuvent obtenir même sans toutes les lignes CONTEXT_LINES disponibles, vous pouvez changer la définition de CONTEXT sera comme suit:

CONTEXT = "((?:.*#{EOL}){0,#{CONTEXT_LINES}})" 

Par défaut, les correspondances sont avides, donc si tout ou partie des lignes CONTEXT_LINES disponibles, c'est ce que vous prenez.