如何提取大型文本文件的一部分,从第一次出现 FOO 开始到第一次出现 BAR 结束?
就我而言,我正在尝试提取由 mysqldump 创建的 sql 文件的一部分。
答案1
致谢@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 和 BAR 分隔的第一个段落之后我们不应该遇到另一个 FOO。
否则,简单的情况已经不再那么简单了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