Search code examples
powershellbatch-filesimulate

How to simulate a batch script


I need to setup a PowerShell script based upon a batch script. The original batch script looks like the following:

call %SYSTEMROOT%\setup_Env.BAT
command_name command_arguments

The command is dependent upon the environmental variables from the setup_ENV.BAT being setup.

$tempFile = [IO.Path]::GetTempFileName()
$script = "%SYSTEMROOT%\setup_Env.BAT"
cmd /c " $script && set > $tempFile "
cmd /c " command_name command_arguments"

I received the error:

cmd : 'command_name is not recognized as an internal or external command,...

If there is a better way to do this in PowerShell, I am open to it.


Solution

  • You need to pass a single command line to cmd to make this work:

    cmd /c "call %SYSTEMROOT%\setup_Env.BAT && command_name command_arguments"
    

    As Ansgar Wiechers points out, every cmd invocation runs in a child process, and any environment modifications made in a child process are not visible to the calling process and therefore also not to future child processes.

    By contrast, in the single command line above, the environment-variable modifications performed by setup_Env.BAT are visible to command_name by the time it executes.


    Caveat: If command_arguments contains %...%-style references to the environment variables defined in setup_Env.BAT, more work is needed:

    • Change the %...%-style references to !...!-style references.
    • Additionally invoke cmd with /v to enable delayed variable expansion (the equivalent of setlocal enabledelayedexpansion inside a script`:

      cmd /v /c "call %SYSTEMROOT%\setup_Env.BAT && command_name args_with_delayed_var_refs"
      

    Caveat: The above may still not work as intended if command_arguments happens to contain ! chars. that should be treated as literals (and/or command_name is another batch file containing such).

    In that event, the simplest approach is to simply recreate the entire batch file in a temporary file and invoke that:

    # Get temp. file path
    $tempBatFile = [IO.Path]::GetTempFileName() + '.bat'
    
    # Write the content of the temp. batch file
    @'
    @echo off
    call %SYSTEMROOT%\setup_Env.BAT
    command_name command_arguments
    '@ | Set-Content $tempBatFile
    
    # Execute it.
    & $tempBatFile 
    
    # Clean up.
    Remove-Item -LiteralPath $tempBatFile