How to properly set a variable from PowerShell in Windows batch file?

后端 未结 3 368
夕颜
夕颜 2021-01-16 14:15

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

PowerShell:

$datePattern = [Regex]::new(\'value=(\\S+)\')
$         


        
相关标签:
3条回答
  • 2021-01-16 14:54

    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" )
    
    0 讨论(0)
  • 2021-01-16 14:55

    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+)'

    0 讨论(0)
  • 2021-01-16 15:00

    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
    
    0 讨论(0)
提交回复
热议问题