In batch files, all shell variables are also environment variables ; therefore setlocal ... endlocal also provides a local scope for environment variables.
Unlike PowerShell , shell variables (like $var ) are different from environment variables (like $env:PATH ) - this is a difference that is usually beneficial.
Given that the smallest area for setting environment variables is the current process - and therefore the entire PowerShell session , you have to manage the small custom area manually if you want to do this in-process (this is what setlocal ... endlocal does in cmd.exe , for which PowerShell does not have a built-in equivalent; for shell variables of a custom field, use & { $var = ...; ... } ):
In-process approach: manual control of a custom area:
To ease the pain a bit, you can use the slightly redesigned cmdlet ForEach-Object call (via its built-in single-character alias % ):
% { $oldVal, $env:MYLANG = $env:MYLANG, 'EN' } { my-cmd dostuff -o out.csv } { $env:MYLANG=$oldVal }
Simply put, if there is no existing MYLANG value that needs to be restored:
% { $env:MYLANG='EN' } { my-cmd dostuff -o out.csv } { $env:MYLANG=$null }
This uses:
ForEach-Object enters its process script block (medium) once once even if there is no pipeline input.
ForEach-Object offers initializing and clearing script blocks (first and last, respectively).
$oldVal, $env:MYLANG = $env:MYLANG, 'EN' saves the old value (if any) $env:MYLANG in $oldVal when changing the value to 'EN' ; this technique of assigning several variables at once (known as the assignment of destructuring in some languages) is explained in Get-Help about_Assignment_Operators , section "PURPOSE OF MULTIPLE OPTIONS".
Note that the helper variable $oldVal remains in scope after this call; if this is a concern, clear it / restore it to its previous value (or select a name that is unlikely to be accidentally used in the surrounding code).
A more correct and reliable, but more detailed solution is to use try { ... } finally { ... } :
try {
To facilitate this approach, this answer on my question offers a convenience feature
Invoke-WithEnvironment , which allows you to write the same call as:
# Define env. var. $env:MYLANG only for the duration of executing the commands
Alternatives using the helper process:
Using the helper process and setting the environment variable here,
you avoid having to recover your environment after a call
but you pay a penalty for performance, and the complexity of the call increases.
Using aux. cmd.exe process:
cmd /c "set `"MYLANG=EN`" & my-cmd dostuff -o out.csv"
Note:
An external "..." clause has been chosen so that you can reference PowerShell variables in your command; built-in " must be escaped `"
In addition, the arguments of the target command must be passed in accordance with the rules of cmd.exe (it does not matter with a simple command).
Using aux. PowerShell child session:
# In PowerShell *Core*, use `pwsh` in lieu of `powershell` powershell -nop -c { $env:MYLANG = 'EN'; my-cmd dostuff -o out.csv }
Note:
Starting another PowerShell session is expensive.
Exiting the script block ( { ... } ) is serialized and then deserialized in the call area; it doesn't matter for string output, but complex objects like [System.IO.FileInfo] deserialized to emulate the originals (which may or may not be a problem).