Usar um analisador XML é realmente uma boa ideia, mas se você não puder usar um por algum motivo (o arquivo não está bem formado, você não tem nenhum analisador instalado, etc.), você pode usar o PERL para isso:
$ perl -ne 'if(/<h2>(.*?)<\/h2><p>(.*?)<\/p>/){print "$1\t$2\n"}' filename.ext
Hello World
Bells Walls
Jelly Minus
Eu prefiro usar lazy matches para não obter resultados indesejados:
test.txt
<h1>Nothing</h1>
<h2>Hello</h2><p>World</p><h2>Goodbye</h2><p>Earth</p>
<h2>Bells</h2><p>Walls</p>
<h2>Jelly</h2><p>Minus</p>
<h3>Zip</h3>
$ perl -ne 'if(/<h2>(.*?)<\/h2><p>(.*?)<\/p>/){print "$1\t$2\n"}' test.txt
Hello World
Bells Walls
Jelly Minus
$ perl -ne 'if(/<h2>(.*)<\/h2><p>(.*)<\/p>/){print "$1\t$2\n"}' test.txt
Hello</h2><p>World</p><h2>Goodbye Earth
Bells Walls
Jelly Minus
Como você pode ver, usar somente uma expressão regular não obterá todos os casos que uma ferramenta específica de domínio usará. Se você está bem com isso, tudo bem; apenas esteja ciente de que você pode obter resultados imprecisos se a entrada não corresponder exatamente ao seu padrão!