Как извлечь часть большого текстового файла, начиная с первого вхождения 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