1

For someone unknown reasons, I am unable to retrieve this variable on Windows batch file.

PowerShell:

$datePattern = [Regex]::new('value=(\S+)')
$datePattern = [Regex]::new('(\d\d\.\d)')
$matches = $datePattern.Matches("/ start=2010 / height=1 / value=12.2 / length=0.60 / users=264 / best=Adam /")
$matches.Value

It works perfectly fine. However, this fails miserably on batch file.

for /f %%i in ("PowerShell -NoProfile -ExecutionPolicy Bypass -Command $datePattern = [Regex]::new(value=(\S+)); $datePattern = [Regex]::new((\d\d\.\d)); $matches = $datePattern.Matches(tmpFile); $matches.Value") do ( set newValue=%%i )

The goal is to be able to have %newValue% return 12.2 from value=. If it can be done directly on batch file, then that's even better. The values differentiate from file to file.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Ricky
  • 33
  • 7
  • The `Matches()` method operates on a *string* (like in your PowerShell example) whereas in your batch example you try to have it operate on a *file*. The latter doesn't work, so your (batch) variable is not set. Is the string in the PowerShell example representative for the content of your files (i.e. are all key=value pairs on the same line)? Or are they one key=value pair per line? Please update your question with a representative input sample. – Ansgar Wiechers Dec 30 '18 at 11:45
  • Thanks for the quick reply. tmpFile generates one long line which is set in batch. But since I can't figure out how to retrieve that value I want directly in batch, I've used PowerShell instead. It's the same format as the sample I provided but with more values that aren't important. I'm trying to retrieve a specific value, value=xx.x in this case by ignoring the rest. TIA – Ricky Dec 30 '18 at 11:59
  • can you post a sample of the data file AND what you want the output to be? this looks like something that could be done fairly easily in powershell ... no need for the back-n-forth with BAT stuff. – Lee_Dailey Dec 30 '18 at 12:20
  • Do you control the format of `tmpFile` or is the file generated by someone/something else? Can you pre-process the file to get a format with one key=value pair per line? – Ansgar Wiechers Dec 30 '18 at 13:18

3 Answers3

1

It's all about proper quoting and escaping. Read powershell -? (excerpt truncated):

-Command

Executes the specified commands (and any parameters) as though they were
typed at the Windows PowerShell command prompt, and then exits, unless
NoExit is specified. The value of Command can be "-", a string. or a
script block.
…
If the value of Command is a string, Command must be the last parameter
in the command , because any characters typed after the command are
interpreted as the command arguments.

To write a string that runs a Windows PowerShell command, use the format:
    "& {<command>}"
where the quotation marks indicate a string and the invoke operator (&)
causes the command to be executed.

Here our <command> contains double quotes:

$datePattern = [Regex]::new('(\d\d\.\d)');$matches = $datePattern.Matches("/ start=2010 / height=1 / value=12.2 / length=0.60 / users=264 / best=Adam /");$matches.Value

Use single quotes instead as follows:

$datePattern = [Regex]::new('(\d\d\.\d)');$matches = $datePattern.Matches('/ start=2010 / height=1 / value=12.2 / length=0.60 / users=264 / best=Adam /');$matches.Value

or, alternatively, double inner double quotes twice:

$datePattern.Matches(""""/ … / value=12.2 / … /"""")

Full powershell call then looks as follows:

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {$datePattern = [Regex]::new('(\d\d\.\d)');$matches = $datePattern.Matches(""""/ start=2010 / height=1 / value=12.2 / length=0.60 / users=264 / best=Adam /"""");$matches.Value}"

Finally, apply FOR /F Loop command: against the results of another command:

for /f "usebackq" %i in (`PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {$datePattern = [Regex]::new('(\d\d\.\d)');$matches = $datePattern.Matches(""""/ start=2010 / height=1 / value=12.2 / length=0.60 / users=264 / best=Adam /"""");$matches.Value}"`) do ( set "newValue=%i" )

The latter command works from a command prompt. Double the % sign in a batch script:

for /f "usebackq" %%i in (`PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {$datePattern = [Regex]::new('(\d\d\.\d)');$matches = $datePattern.Matches(""""/ start=2010 / height=1 / value=12.2 / length=0.60 / users=264 / best=Adam /"""");$matches.Value}"`) do ( set "newValue=%%i" )
JosefZ
  • 28,460
  • 5
  • 44
  • 83
0

I'm not quite sure what exactly it is you want to get from the regex, but in your code you are re-defining the $datePattern immediately after setting it to value=(\S+).

With \S+ you seem to not care about the format of the value, as long as it is something that is not whitespace. In the second definition of the regex, you DO want the value to be exactly two digits followed by a dot and then another digit.

If that exact number format is what you seek, just do

$datePattern = [Regex]::new('value=(\d\d\.\d)')

When executed with

$m = $datePattern.Matches("/ start=2010 / height=1 / value=12.2 / length=0.60 / users=264 / best=Adam /")

you will have $m.Value which results in value=12.2
and $m.Groups[1].Value which gives you the result of 12.2

If you are looking for a numeric value but do not know the exact format, better change the regex to something like this: 'value=(\d+.\d+)'

Theo
  • 57,719
  • 8
  • 24
  • 41
  • Yes, that also works on PowerShell. My question is how to be able to use that on Windows Batch? TIA – Ricky Dec 30 '18 at 12:25
0

Provided the file tmpfile IS a single line file and doesn't exceed max cmd line length.

Batch

:: Q:\Test\2018\12\30\SO_53977221_.cmd
@Echo off
set /P "string="<tmpfile
set "string=%string:*value=%"
set "newvalue=%string:~1,4%"
set newvalue

>  SO_53977221_.cmd
newvalue=12.2

This uses string substitution (with a wildcard) and substring to get the desried value.

A combined batch/powershell solution is also possible but requires a bit more effort with escaping and quoting than your try.

This PowerShell one liner will output

PoSh> if((Get-Content .\tmpfile) -match 'value=(\d\d\.\d)'){$Matches[1]}
12.2

Properly wrapped in batch

:: Q:\Test\2018\12\30\SO_53977221.cmd
@Echo off
for /f "usebackq" %%A in (`
    powershell -NoP -C "if((Get-Content .\tmpfile) -match 'value=(\d\d\.\d)'){$Matches[1]}"
`) Do set "newvalue=%%A"
set newvalue

Sample output:

> .\SO_53977221.cmd
newvalue=12.2
  • Yes, that's exactly what I need. But it keeps returning 0 for me? – Ricky Dec 30 '18 at 12:33
  • Yes, both work perfectly now :D For the batch one, I had to add an extra space for it not to get mixed up: set "string=%string:* value=%" – Ricky Dec 30 '18 at 12:54
  • If an answer solves your question or you find it helpful you should consider to [accept the answer](http://stackoverflow.com/help/accepted-answer) and/or [vote up](https://stackoverflow.com/help/why-vote) –  Dec 30 '18 at 13:39
  • Yes, thank you. First time using it, haven't adjusted yet. Thanks for the feedback! Votes cast by those with less than 15 reputation are recorded, but do not change the publicly displayed post score. – Ricky Dec 30 '18 at 13:44