Grep търсене на низове с прекъсвания на редове

Как да използвам grep за извеждане на срещания низ "export to excel" във входните файлове, дадени по-долу? По-конкретно, как да се справим с прекъсванията на редовете, които се случват между низовете за търсене? Има ли превключвател в grep, който може да изпълни тази или друга команда вероятно?

Входни файлове:

Файл a.txt:

бла бла ... експортиране в
excel ...
бла бла..

Файл b.txt:

бла бла ... експорт в excel ...
бла бла..


person Vijay Dev    schedule 07.12.2009    source източник
comment
Доколкото разбирам (референция: Unix Power Tools), фамилията програми grep са ориентирани към ред, четат ред по ред и следователно не могат да намерят шаблони през ред. Така че можете да помислите за perl скрипт или да използвате sed тук. HTH.   -  person sateesh    schedule 07.12.2009
comment
как да използвам sed в този контекст?   -  person Vijay Dev    schedule 07.12.2009
comment
@Vijay: echo -e foo\nbar | sed -n 'N;/foo\nbar/p'   -  person SiegeX    schedule 07.12.2009
comment
@SiegeX: Това не намира foo bar. Вижте отговора ми по-долу.   -  person Dennis Williamson    schedule 07.12.2009
comment
@Dennis: Да, знам; той намира foo\nbar, което беше демонстрация на Vijay за това как sed може да се използва за улавяне на низове с нови редове между тях.   -  person SiegeX    schedule 08.12.2009


Отговори (5)


Искате ли просто да намерите файлове, които съдържат шаблона, игнорирайки прекъсванията на редовете, или искате действително да видите съответстващите редове?

Ако е първото, можете да използвате tr за преобразуване на нови редове в интервали:

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

Ако последното, можете да направите същото, но може да искате да използвате флага -o, за да отпечатате само действителното съвпадение. След това ще искате да коригирате своя регулярен израз, за ​​да включите всеки допълнителен контекст, който искате.

person Laurence Gonsalves    schedule 07.12.2009
comment
tr + grep решението не е много подходящо за големи файлове, тъй като ще формира един ГОЛЯМ низ. - person ghostdog74; 07.12.2009

Не знам как да направя това в grep. Проверих man страницата за egrep(1) и тя също не може да съвпада с нов ред в средата.

Харесва ми решението, предложено от @Laurence Gonsalves, за използване на tr(1) за изтриване на новите редове. Но както той отбеляза, ще бъде трудно да отпечатате съвпадащите редове, ако го направите по този начин.

Ако искате да съпоставите въпреки нов ред и след това да отпечатате съвпадащите редове, не мога да измисля начин да го направя с grep, но няма да е твърде трудно в някой от Python, AWK, Perl или Ruby .

Ето скрипт на Python, който решава проблема. Реших, че за редове, които съвпадат само когато са съединени с предишния ред, ще отпечатам стрелка --> преди втория ред на съвпадението. Редовете, които съвпадат напълно, винаги се отпечатват без стрелката.

Това е написано, като се приема, че /usr/bin/python е Python 2.x. Можете тривиално да промените скрипта, за да работи под Python 3.x, ако желаете.

#!/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])

РЕДАКТИРАНЕ: добавени коментари.

Направих някои проблеми, за да го накарам да отпечата правилния номер на ред на всеки ред, използвайки формат, подобен на този, който бихте получили с grep -Hn.

Може да е много по-кратко и по-просто, ако нямате нужда от номера на редове и нямате нищо против да прочетете целия файл наведнъж в паметта:

#!/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])
person steveha    schedule 07.12.2009
comment
не виждам да сте компилирал регулярния израз с re.MULTILINE, така че как се проверява за excel на друг ред? - person ghostdog74; 07.12.2009
comment
re.MULTILINE не беше това, което исках, така че не го уточних. С re.MULTILINE кодът re третира новия ред като края на низ и не съвпада след това. Исках нов ред да се третира като всяко друго празно пространство в съвпадението. Ще добавя някои коментари към кода. - person steveha; 07.12.2009
comment
Всъщност първата ми версия щеше да работи по същия начин със или без re.MULTILINE. Втората версия на slurp-in-whole-file не трябва да има този флаг, защото зависи от съвпадението около нов ред. Първата версия изгражда специален единичен ред и премахва всеки нов ред в процеса. - person steveha; 07.12.2009

grep -A1 "експорт към" име на файл | grep -B1 "excel"

person christian.buggle    schedule 05.05.2012
comment
Това решение не гарантира, че експортирането към е до excel. Ще съвпадне, например, с експорт в\nбла бла бла бла excel. - person stepthom; 13.08.2012
comment
Освен това не съответства на експортиране\nв excel и не се мащабира за търсене на низ, който съдържа много интервали. - person ; 02.02.2015

Тествах това малко и изглежда работи:

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

Можете да оставите допълнително бяло пространство в края и началото на редовете по следния начин:

sed -n '$b; /export to excel/{p; b}; N; /export to\s*\n\s*excel/{p; b}; D' filename
person Dennis Williamson    schedule 07.12.2009

използвай gawk. задайте разделител на записи като excel, след което проверете за "експортиране към".

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

or

gawk '/export.*to.*excel/{print}
/export to/&&!/excel/{
  s=$0
  getline line
  if (line~/excel/){
   printf "%s\n%s\n",s,line
  } 
}' file
person ghostdog74    schedule 07.12.2009
comment
Как бихте отпечатали действителните редове, както би направил grep (за съвпадения в неговите възможности)? - person Dennis Williamson; 07.12.2009
comment
отпечатайте записа, $0. Иначе не разбирам какво имаш предвид. - person ghostdog74; 07.12.2009
comment
Мисля, че вашата редакция се грижи за това. Въпреки това, той се проваля за някои крайни случаи. Ако входът беше нещо като експортиране на excel в\nexcel или експортиране в\nнещо различно от excel, например. За да отговоря на въпроса ви във вашия коментар: оригиналният едноредов ред, ако $0 бяха добавени към изхода, няма да покаже excel и особено ... след него, което е посочено във въпроса на OP. - person Dennis Williamson; 07.12.2009