Извлечь часть текстового файла от первого вхождения одной строки до первого вхождения другой

Извлечь часть текстового файла от первого вхождения одной строки до первого вхождения другой

Как извлечь часть большого текстового файла, начиная с первого вхождения FOO и заканчивая первым вхождением BAR?

В моем случае я пытаюсь извлечь часть SQL-файла, созданного mysqldump.

решение1

Кредиты для@dgigи@Паулокоторые помогли мне своими отзывами!Финал perlздесь в одну строку:

perl -lne 'if(/FOO/../BAR/){s/.*?(FOO)/$1/ if!$i++;s/BAR\K.*//&&print&&exit;print}' file

Объяснение:

if(/FOO/../BAR/){        # perform the following actions on each line, starting
                         # with a line that contains FOO, and up to and including
                         # a line that contains BAR  
s/.*?(FOO)/$1/ if!$i++;  # only on the first line that contains FOO,
                         # delete all characters before FOO  
s/BAR\K.*//&&print&&exit;# if the line contains BAR, remove characters
                         # after BAR, print the line and stop processing  
print                    # simply print the line contents

Старый ответ:

Кредиты для@Паулодля простого sedрешения. Это так же просто и легко читать в awk:

awk '/FOO/,/BAR/' file

Хотя это может быть слишком просто: он возвращает целые строки, а не совсем "часть текста, начинающуюся с первого появления FOO и заканчивающуюся первым появлением BAR". Я считаю, что это означает, что FOO должно быть первым словом, а BAR последним. Чтобы сделать именно это, требуется более замысловатый ответ. Позвольте мне попробовать сделать это в perl.

Простой случай (возвращает целые строки):

perl -lne 'print if /FOO/../BAR/' file

Сложный случай (точно от FOO до BAR):

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//;print}' file

Мне нравится это эквивалентное решение, которое присваивает переменную оператору диапазона:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*// if$a=~/E/;print}' file

Примечание:Предполагается, что необходимо извлечь только одну часть текста, т.е. мы не должны встретить еще один FOO после первого абзаца, разделенного FOO и BAR.

В противном случае простой случай уже не так прост awk:

awk '/FOO/,/BAR/ {print; if ($0~/BAR/) {exit} }' file

И в perl:

perl -lne '(print&&/BAR/&&exit) if /FOO/../BAR/' file

А более сложные, более совершенные решения становятся:

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//&&print&&exit;print}' file

и:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*//&&print&&exit if$a=~/E/;print}' file

Этот пример показывает, как однострочный код может превратиться из исключительно ясного и самоочевидного в то, что выглядит как непонятная последовательность случайных символов, лишь немного усложнив проблему. При необходимости я бы рекомендовал писать автономный, поддерживаемый, читаемый скрипт, в который можно легко добавлять дополнительные функции и учитывать пограничные случаи.

решение2

В этом случае это было не так сложно, как я думал. С sed, от первого появления FOO до первого появления BAR (я не пробовал, но, вероятно, что-то вроде второго FOO до второго BAR было бы сложнее.)

sed -nr '/FOO/ {
/FOO/ s/[^F]+FOO/FOO/p
:a
n
/BAR/ s/([^B]+BAR).*/\1/
p
/BAR/ q
ba
}' <<<'line1
> line2 FOO text1 FOO text2
> line3
> line4 BAR text3 BAR text4
> line5'

FOO text1 FOO text2
line3
line4 BAR

Связанный контент