Regex for getting text between two texts using command prompt

Viewed 144

I am trying to extract the contents with in the body tag in my html file using command prompt and findstr command. My html is as below

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AdminWeb</title>
  <base href="/wwwroot/admin-web/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body><app-root></app-root><script src="/wwwroot/admin-web/runtime.js"></script><script src="/wwwroot/admin-web/file1.js" nomodule></script><script src="/wwwroot/admin-web/file2.js"></script><script src="/wwwroot/admin-web/styles.js"></script><script src="/wwwroot/admin-web/vendor.js"></script><script src="/wwwroot/admin-web/main.js"></script></body>
</html>

The out put I want is

<app-root></app-root>
<script src="/wwwroot/admin-web/runtime.js"></script><script src="/wwwroot/admin-web/file1.js" nomodule></script><script src="/wwwroot/admin-web/file2.js"></script><script src="/wwwroot/admin-web/styles.js"></script><script src="/wwwroot/admin-web/vendor.js"></script><script src="/wwwroot/admin-web/main.js"></script>

I am trying to achieve using regular expression.

findstr /R (?<=<body>)(.*)(?=</body>)  test.html

But this is now working in command prompt. But this regex is working in js.

Thanks in advance.

2 Answers

First of all, findstr does not support all cool regex features that modern regex engines offer. Especially the latest JavaScript ECMAScript2018+ compliant engines like in Chrome, Node.js, etc. So, saying that "this regex is working in js" does not mean the same pattern will work anywhere else. It won't certainly work in findstr.

You may take the hard way and go on to study how to write a batch script for this. However, there is a much simpler way with other built-in Windows apps.

I strongly suggest Powershell as it offers you a lot of features .NET provides.

Here, open PowerShell console and use

$pathToFile = 'c:\...\...\you_file.txt'
$output_file = 'c:\...\...\you_file_out.txt' 
$rx = '(?s)(?<=<body>).*?(?=</body>)'
Get-Content $pathToFile -Raw | Select-String $rx -AllMatches | % { $_.Matches } | % { $_.Value } > $output_file

NOTE: It is best to use an IE automation to handle HTML.

$output_file = 'c:\...\...\you_file_out.txt'
$url = 'http://your_site_here.tld/...'
$ie = New-Object -comobject "InternetExplorer.Application"
$ie.visible = $true
$ie.navigate($url)

while ($ie.Busy -eq $true -Or $ie.ReadyState -ne 4) {Start-Sleep 2}

$doc = $ie.Document
$tags = $doc.getElementsByTagName("body")
$tags[0].innerHTML > $output_file

Windows 10 64-bit. PowerShell 5.1

Parsing local \ remote html with PowerShell 5.1 match regex.

Output everything between the body tags. Do not include / include <body> tags in results. See working on a local / remote file below. See find anything between any <tag> and the very first instance of </tag> below.

Greedy and lazy quantifiers explained

regex101.com is your friend.

Never use (.?|\n), always use (?s).*?

Copy and paste into PowerShell 5.1. It will output everything between <body></body>. The results outputs twice, once without the body tags and once with.

$testhtml = @'
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AdminWeb</title>
<base href="/wwwroot/admin-web/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<script src="/wwwroot/admin-web/runtime.js"></script><script src="/wwwroot/admin-web/file1.js" nomodule></script>
<script src="/wwwroot/admin-web/file2.js"></script><script src="/wwwroot/admin-web/styles.js"></script>
<script src="/wwwroot/admin-web/vendor.js"></script><script src="/wwwroot/admin-web/main.js"></script>
</body>
</html>
'@

$testhtml -match '(?s)(?<=<body>).*?(?=<\/body>)';$matches[0] 
$testhtml -match '(?s)(<body>).*?(<\/body>)';$matches[0] 

The command can be written:

[regex]::matches($testhtml,'(?s)(?<=<body>).*?(?=<\/body>)').Value
[regex]::matches($testhtml,'(?s)(<body>).*?(<\/body>)').Value

Three different commands to work on a local file. Output does not include body tags:

(gc -Raw test.html) -match '(?s)(?<=<body>).*?(?=<\/body>)';$matches[0] 

[Regex]::Matches((gc -Raw test.html), '(?s)(?<=<body>).*?(?=<\/body>)').Value

gc test.html -Raw | Select-String '(?s)(?<=<body>).*?(?=<\/body>)' -AllMatches | % { $_.Matches } | % { $_.Value }

Output to results.html:

gc test.html -Raw | Select-String '(?s)(?<=<body>).*?(?=<\/body>)' -AllMatches | % { $_.Matches } | % { $_.Value } | sc results.html 

Remote url without and with body tag:

$testhtml = Invoke-WebRequest -Uri http://www.pgatour.com
$testhtml -match '(?s)(?<=<body>).*?(?=<\/body>)'
$matches[0]
$testhtml -match '(?s)(<body>).*?(<\/body>)'
$matches[0]

Remote url without and with body tag. Output to results.html:

$testhtml = Invoke-WebRequest -Uri http://www.pgatour.com
$testhtml -match '(?s)(?<=<body>).*?(?=<\/body>)'
$matches[0] | sc results.html
$testhtml -match '(?s)(<body>).*?(<\/body>)'
$matches[0] | sc results.html

Remote url between any tag and the first instance of /tag

$testhtml = Invoke-WebRequest -Uri http://www.pgatour.com
$testhtml -match '(<div class="success-message">)(?s).*?(<\/div>)'
$matches[0]

Invoke-WebRequest -Uri http://www.pgatour.com | sc results.html
(gc -raw results.html) -match '(<div class="success-message">)(?s).*?(<\/div>)'
$matches[0] 

Remote url by classname. These work but are not very productive. See Parsing HTML Webpages with Powershell

$response = Invoke-WebRequest -Uri http://www.pgatour.com
$response.ParsedHtml.body.getElementsByClassName('success-message')
$( $response.ParsedHtml.getElementsByTagName('div') | ?{$_.className -eq 'success-message'} ).outerHTML 
$( $response.ParsedHtml.body.getElementsByTagName('div') | Where {$_.getAttributeNode('class').Value -eq 'success-message'} ).outerHTML 
Related