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