Извлечь часть текстового файла от первого вхождения одной строки до первого вхождения другой
Как извлечь часть большого текстового файла, начиная с первого появления FOO и заканчивая первым появлением BAR?
В моем случае я пытаюсь извлечь часть файла sql, созданного mysqldump.
2 ответа
Кредиты dgig и Paulo, которые помогли мне с их отзывами! окончательный 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
Старый ответ:
Кредиты 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
Этот пример показывает, как однострочник может перейти от исключительно ясного и понятного к тому, что выглядит как неясная последовательность случайных символов, добавив немного больше сложности к проблеме. Везде, где это необходимо, я бы порекомендовал написать отдельный, поддерживаемый, читабельный сценарий, в котором можно легко добавлять дополнительные функции и учитывать все случаи.
В этом случае это было не так сложно, я думал, что это может быть. С 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