Строки поиска Grep с разрывами строк

Как использовать grep для вывода вхождений строки «экспорт в excel» во входных файлах, указанных ниже? В частности, как обрабатывать разрывы строк между строками поиска? Есть ли переключатель в grep, который может выполнять эту или какую-то другую команду?

Входные файлы:

Файл а.txt:

бла-бла... экспортировать в
excel...
бла-бла..

Файл b.txt:

бла-бла... экспортировать в excel...
бла-бла..


person Vijay Dev    schedule 07.12.2009    source источник
comment
Насколько я понимаю (ссылка: Unix Power Tools), семейство программ grep ориентировано на строку, считывает строку за раз и, следовательно, не может найти шаблоны по строке. Таким образом, вы можете придумать Perl-скрипт или использовать sed здесь. ХТН.   -  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
@ Деннис: Да, я знаю; он находит foo\nbar, который был демонстрацией для Виджая того, как можно использовать 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. Я проверил справочную страницу для 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-full-file, не должна иметь этот флаг, потому что она зависит от сопоставления с новой строкой. Первая версия строит специальную единственную строку и удаляет любую новую строку в процессе. - person steveha; 07.12.2009

grep -A1 "экспортировать в" имя файла | grep -B1 "превосходство"

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

используй гавк. установите разделитель записей как 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 и особенно ... после него, что указано в вопросе ОП. - person Dennis Williamson; 07.12.2009