2009-12-07 5 views
9

Comment utiliser grep pour sortir les occurrences de la chaîne 'export to excel' dans les fichiers d'entrée donnés ci-dessous? Plus précisément, comment gérer les sauts de ligne qui se produisent entre les chaînes de recherche? Y at-il un commutateur dans grep qui peut faire ceci ou une autre commande probablement?Grep rechercher des chaînes avec des sauts de ligne

fichiers d'entrée:

fichier A.txt:

bla bla ... à l'exportation
excel ...
bla bla ..

fichier b. txt:

bla bla. .. exporter vers Excel ...
bla bla ..

+0

Si je comprends bien (référence: Unix Power Tools) famille de programmes grep sont orientés ligne, lire une ligne à la fois et ne peut donc pas trouver des modèles à travers la ligne. Vous pouvez donc penser à un script Perl ou utiliser Sed ici. HTH. – sateesh

+0

comment utiliser Sed dans ce contexte? –

+0

@Vijay: echo -e "foo \ nbar" | sed -n 'N;/foo \ nbar/p' – SiegeX

Répondre

6

Voulez-vous simplement trouver des fichiers qui contiennent le motif, en ignorant linebreaks, ou voulez-vous vraiment voir les lignes de correspondance?

Si l'ancien, vous pouvez utiliser tr pour convertir les nouvelles lignes aux espaces:

tr '\n' ' ' | grep 'export to excel' 

Dans ce dernier cas, vous pouvez faire la même chose, mais vous pouvez utiliser le drapeau -o pour imprimer seulement le réel rencontre. Vous devrez ensuite ajuster votre regex pour inclure tout contexte supplémentaire souhaité.

+3

tr + grep solution pas vraiment adapté pour les gros fichiers car il va former une grande chaîne. – ghostdog74

0

utilisez gawk. Définir le séparateur d'enregistrement comme Excel, puis vérifier pour "exporter vers".

gawk -vRS="excel" '/export.*to/{print "found export to excel at record: "NR}' file 

ou

gawk '/export.*to.*excel/{print} 
/export to/&&!/excel/{ 
    s=$0 
    getline line 
    if (line~/excel/){ 
    printf "%s\n%s\n",s,line 
    } 
}' file 
+0

Comment voulez-vous imprimer les lignes réelles comme le ferait 'grep' (pour les correspondances dans sa capacité)? –

+0

imprimer la fiche, 0 $. Sinon, je ne comprends pas ce que tu veux dire. – ghostdog74

+0

Je pense que votre édition prend soin de cela. Cependant, il échoue pour certains cas de bordure. Si l'entrée était quelque chose comme "excel export to \ nexcel" ou "exporter à \ nquelquechose autre qu'excel", par exemple. Pour répondre à votre question dans votre commentaire: le doublage original, si $ 0 a été ajouté à la sortie, ne montrerait pas le "excel" et surtout le "..." après celui qui est indiqué dans la question OP. –

0

J'ai testé un peu et il semble fonctionner:

sed -n '$b; /export to excel/{p; b}; N; /export to\nexcel/{p; b}; D' filename 

Vous pouvez permettre un espace blanc supplémentaire à la fin et au début des lignes comme ceci:

sed -n '$b; /export to excel/{p; b}; N; /export to\s*\n\s*excel/{p; b}; D' filename 
2

Je ne sais pas comment faire ça dans grep. J'ai vérifié la page man pour egrep(1) et il ne peut pas non plus correspondre avec une nouvelle ligne dans le milieu. J'aime la solution proposée par @Laurence Gonsalves, d'utiliser tr(1) pour effacer les retours à la ligne. Mais comme il l'a noté, il sera difficile d'imprimer les lignes correspondantes si vous le faites de cette façon.

Si vous voulez faire correspondre malgré un retour à la ligne et ensuite imprimer les lignes correspondantes, je ne peux pas penser à un moyen de le faire avec grep, mais ce ne serait pas trop difficile dans tout de Python, AWK, Perl, ou Ruby.

Voici un script Python qui résout le problème. J'ai décidé que, pour les lignes qui ne correspondent que lorsqu'elles sont jointes à la ligne précédente, j'imprimerais une flèche --> avant la deuxième ligne du match. Les lignes qui correspondent parfaitement sont toujours imprimées sans la flèche.

Ceci est écrit en supposant que/usr/bin/python est Python 2.x.Vous pouvez modifier le script de manière triviale pour travailler sous Python 3.x si vous le souhaitez.

#!/usr/bin/python 

import re 
import sys 

s_pat = "export\s+to\s+excel" 
pat = re.compile(s_pat) 

def print_ete(fname): 
    try: 
     f = open(fname, "rt") 
    except IOError: 
     sys.stderr.write('print_ete: unable to open file "%s"\n' % fname) 
     sys.exit(2) 

    prev_line = "" 
    i_last = -10 
    for i, line in enumerate(f): 
     # is ete within current line? 
     if pat.search(line): 
      print "%s:%d: %s" % (fname, i+1, line.strip()) 
      i_last = i 
     else: 
      # construct extended line that included previous 
      # note newline is stripped 
      s = prev_line.strip("\n") + " " + line 
      # is ete within extended line? 
      if pat.search(s): 
       # matched ete in extended so want both lines printed 
       # did we print prev line? 
       if not i_last == (i - 1): 
        # no so print it now 
        print "%s:%d: %s" % (fname, i, prev_line.strip()) 
       # print cur line with special marker 
       print "--> %s:%d: %s" % (fname, i+1, line.strip()) 
       i_last = i 
     # make sure we don't match ete twice 
     prev_line = re.sub(pat, "", line) 

try: 
    if sys.argv[1] in ("-h", "--help"): 
     raise IndexError # print help 
except IndexError: 
    sys.stderr.write("print_ete <filename>\n") 
    sys.stderr.write('grep-like tool to print lines matching "%s"\n' % 
      "export to excel") 
    sys.exit(1) 

print_ete(sys.argv[1]) 

EDIT: commentaires ajoutés.

J'ai eu des problèmes pour imprimer le bon numéro de ligne sur chaque ligne, en utilisant un format similaire à celui que vous obtiendriez avec grep -Hn.

Il pourrait être beaucoup plus court et plus simple si vous n'avez pas besoin des numéros de ligne, et vous ne faites pas attention à lire dans le fichier entier à la fois dans la mémoire:

#!/usr/bin/python 

import re 
import sys 

# This pattern not compiled with re.MULTILINE on purpose. 
# We *want* the \s pattern to match a newline here so it can 
# match across multiple lines. 
# Note the match group that gathers text around ete pattern uses a character 
# class that matches anything but "\n", to grab text around ete. 
s_pat = "([^\n]*export\s+to\s+excel[^\n]*)" 
pat = re.compile(s_pat) 

def print_ete(fname): 
    try: 
     text = open(fname, "rt").read() 
    except IOError: 
     sys.stderr.write('print_ete: unable to open file "%s"\n' % fname) 
     sys.exit(2) 

    for s_match in re.findall(pat, text): 
     print s_match 

try: 
    if sys.argv[1] in ("-h", "--help"): 
     raise IndexError # print help 
except IndexError: 
    sys.stderr.write("print_ete <filename>\n") 
    sys.stderr.write('grep-like tool to print lines matching "%s"\n' % 
      "export to excel") 
    sys.exit(1) 

print_ete(sys.argv[1]) 
+0

Je ne vous vois pas compilé l'expression rationnelle avec re.MULTILINE, alors comment vérifier "excel" sur une autre ligne? – ghostdog74

+0

re.MULTILINE n'était * pas * ce que je voulais, donc je ne l'ai pas spécifié. Avec re.MULTILINE, le code 're' traite une nouvelle ligne comme la fin d'une chaîne, et ne correspond pas après cela. Je voulais une nouvelle ligne traitée comme n'importe quel autre espace blanc dans la correspondance. Je vais ajouter quelques commentaires au code. – steveha

+0

En fait, ma première version fonctionnerait de la même manière avec ou sans la re.MULTILINE. La seconde version, slurp-in-whole-file, ne doit pas avoir ce flag car elle dépend de la correspondance autour d'un newline. La première version construit une ligne unique spéciale et supprime toute nouvelle ligne dans le processus. – steveha

1

grep -A1 « à l'exportation » nom de fichier | grep -B1 "excel"

+2

Cette solution ne garantit pas que "exporter vers" est à côté de "Excel". Il va correspondre, par exemple, "exporter vers \ nblah bla bla bla excel". – stepthom

+0

Il ne correspond pas non plus à "export \ nto excel" et ne correspond pas à la recherche d'une chaîne contenant beaucoup d'espaces. – Keelan

Questions connexes