Here is my batch file:
CD c:/
PAUSE
When run in Windows PowerShell, I get this output:
PS C:\some\path\here> .\myscript.bat
C:\some\path\here>CD c:/
c:\>PAUSE
Press any key to continue . . .
PS C:\some\path\here>
After the program terminates, I'm back where I started!
Meanwhile, when run in cmd:
C:\some\path\here>.\myscript.bat
C:\some\path\here>CD c:/
c:\>PAUSE
Press any key to continue . . .
c:\>
I get the behavior I want, where I remain in the C:\ directory after the program ends.
Why is this the case, and how can I get this behavior in PowerShell too?
Running a batch file (*.cmd or *.bat) from PowerShell of necessity involves a cmd.exe child process. (PowerShell itself doesn't understand batch files, only the batch-file interpreter, cmd.exe does; as a separate executable, it invariably runs in a child process.)
A child process fundamentally cannot set its parent's working directory (nor can it modify its environment variables). Thus, whatever a batch file sets as its working directory has no effect on a PowerShell caller.
It follows that changing the caller's working directory is only possible via code that runs in-process.
Both cmd.exe and PowerShell run their script files - batch files (*.cmd, *.bat) and PowerShell scripts (*.ps1), respectively - in-process, enabling script files to change the process' working directory session-globally in their native shell - this is what you saw when you called your batch file from a cmd.exe session.
In PowerShell, you can create a *.ps1 file with the same content as your batch file (in this case), and calling it (e.g. .\myscript.ps1) will change the PowerShell session's working directory.
Conversely, if you want to scope (limit) the working-directory change to a given script file, so that the original working directory is restored on exiting the script:
In cmd.exe, you can achieve this by enclosing batch-file code in setlocal and endlocal statements (the latter is often omitted, because it is implied upon exiting a batch file), which not only restores the original working directory, but also the original environment variables.
PowerShell offers no analogous mechanism,[1] so the previous state must be saved and restored manually:
To restore the previous working directory (location), you must use the following idiom:
$oldPwd = $PWD
try {
# ... script code that changes the working dir. goes here
} finally { Set-Location -LiteralPath $oldPwd }
That said, it is for this reason that Set-Location (cd) calls are best avoided in PowerShell scripts.
[1] However, PowerShell by default scopes its shell variables (and other definitions, such as functions) defined in a script to that script. Shell variables (e.g. $foo) are PowerShell-specific variables that are distinct from environment variables (which in PowerShell are referenced with prefix $env:); modifying the latter does take effect process-globally (and affects any child processes, which inherit copies of them) and - unlike in batch files - there's no scoping mechanism.
Note that cmd.exe makes no distinction between shell and environment variables: any variable that is defined is implicitly an environment variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With