1

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

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

2 ответа2

2

Кредиты @dgig и @Paulo, которые помогли мне с их отзывами! Финальный perl -Liner здесь:

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

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

Кредиты @Paulo для простого решения 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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .