2

I have a batch script that is not able to be changed as it is owned by another application. I want to call that batch script from Powershell and access the variable values. This looks to be a scope issue and maybe I am not calling the batch script correctly. I have tried "&", "invoke-expression", "cmd /c" and "start-process" but the result is the same. Here is the simplified problem.

I have a batch script called batch.bat containing two lines:

set /p variable=what is the fruit:
echo %variable%    (from batch)

I have a powershell script containing:

$fp=resolve-path(".\batch.bat")
Invoke-Expression -Command "$fp"
write-host "$env:variable    (from PS)"

from cmd prompt:

set variable=banana
powershell .\script.ps1
what is the fruit: orange
orange    (from batch)
banana    (from PS)

I want orange to be available to PS. If I hadn't preset the variable to banana, then PS would not display anything so it has nothing to do with presetting the value. I considered that I could write another wrapper command script that executes the original batch script, then writes out all the variables (in PS format) to a temp .ps1 file and then execute (dot source) that but it seems clunky.

mklement0
  • 382,024
  • 64
  • 607
  • 775
madturbocow
  • 119
  • 6
  • As an aside: PowerShell functions, cmdlets, scripts, and external programs must be invoked _like shell commands_ - `esolve-path ".\batch.bat"` - _not_ like C# methods - `resolve-path(".\batch.bat")`. It happens to work in this particular, single-argument case - but it'll fail with multiple arguments - see [this answer](https://stackoverflow.com/a/65208621/45375) for details. – mklement0 Mar 08 '23 at 03:01
  • As an aside: [`Invoke-Expression` (`iex`) should generally be avoided](https://stackoverflow.com/a/51252636/45375); definitely [don't use it to invoke an external program, batch file, or PowerShell script / command](https://stackoverflow.com/a/57966347/45375). – mklement0 Mar 08 '23 at 03:02
  • @Compo, I've fixed the syntax error (`\p` -> `/p`). – mklement0 Mar 08 '23 at 03:28
  • 1
    Yes, sorry I made the mistake in transcribing. It is "/p" in the bat files. – madturbocow Mar 08 '23 at 03:40

1 Answers1

2

Calling a batch file from PowerShell of necessity executes the batch file in a child process, given that a cmd.exe child process must be launched to execute it.

Child processes fundamentally cannot modify their caller's environment variables.

Your best bet is to call the batch file explicitly via cmd.exe /v /c and then echo the values of the environment variables of interest, which allows PowerShell to capture them:

$variableValue = cmd /v /c '.\batch.bat >&2 & echo !variable!'
"Value of %variable%: $variableValue"
  • /v is needed to enable delayed variable expansion, which allows the echo command that is part of the same statement that invokes .\batch.bat to dynamically report values that were set by the latter, using !...! rather than %...% to enclose the variable reference.

    • Note that this can affect the execution of .\batch.bat, in that ! characters then become metacharacters, which - unless they deliberately enclose a variable reference - are quietly removed.
    • See the next section for an alternative solution that avoids this problem.
  • >&2 is used to redirect .\batch.bat's stdout output to stderr (stream 2) so that it still displays, but doesn't interfere with what the cmd /v c outputs to stdout, which is what PowerShell captures.


If you want to echo all variables that exist after the batch file has exited, as <name>=<value> pairs, you use the following, which parses the name-value pairs into a single hasthable, via ConvertFrom-StringData:

# Assign to a variable (e.g, $hash = ...) as needed.
# You can then use $hash.variable to get the value of %variable%, for instance.
(cmd /c '.\batch.bat >&2 & set' | Out-String) -replace '\\', '\\' |
  ConvertFrom-StringData

set without an argument outputs all variables as name-value paris, but you can more selective, by using set <variableNamePrefix> and/or multiple, &-chained set statements with exact variable names (e.g. set variable).

Note: -replace '\\', '\\' looks like a no-op, but it replaces \ instances with \\, because a single \ is interpreted as an escape character by ConvertFrom-StringData.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Unfortunately I have many variables to return but I will take this as the answer. I'll create a new temporary batch script, then from this temp batch script, it will CALL the original (batch.bat) and then do "set > tempfile.txt". Then I can use PS to parse that file and pick the variables that I am after. Thanks. – madturbocow Mar 08 '23 at 03:39
  • @madturbocow, that would work, but there's a simpler solution - please see my update. – mklement0 Mar 08 '23 at 03:52