PowerShell script working directory (current location)

12,652

When PowerShell directly invokes a *.ps1 script, that script runs in-process, in the same runspace as the caller, and therefore the script by default sees the same current location (working directory) as the caller.

As an aside: Conversely, if a script changes the current location, the caller sees that too, after the script exits (see below for details).

If you do not see this behavior, the implication is that some behind-the-scenes code is changing the current location.


The only way I can realistically see this happen is in the following scenarios:

  • If (a) your $PROFILE file contains a Set-Location / Push-Location command that explicitly switches to your home folder or (b) (as in your case, as we now know) if there is - broken - code in an auto-loading module that does that on (automatic) import[1] and:

    • You call PowerShell via its CLI (powershell.exe for Windows PowerShell, pwsh for PowerShell Core) without the -NoProfile switch; e.g. powershell -File someScript.ps1

    • On Unix-like platforms, your script is implemented and called as an extension-less, executable shell script with a shebang line (that doesn't include -NoProfile - see below for details).

  • If you run your script via one of the following commands / features:

    • Via Start-Job (in a child process) or Start-ThreadJob (in a new runspace), at least currently (PowerShell Core 7.0.0-preview.4)

      • Note: These two cmdlets don't behave exactly the same, which is problematic in and of itself, but the larger problem is that they don't simply inherit the caller's current location - see this GitHub issue.
    • Via the PowerShell SDK, in a new runspace.

Other than that, I can only see accidental event-based code producing your symptom, such as the following - pointless - redefinition of the interactive prompt-string defining function, prompt, to make it quietly switch to the $HOME directory after every command:

# !! Obviously, do NOT do this.
function prompt { 
  # Print the usual prompt string.
  "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
  # Quietly return to $HOME
  Set-Location $HOME 
}

The rest of the answer discusses a related scenario that may be of interest.


Executing a PowerShell script with its own directory as the working directory (current location):

In PowerShell v3+, automatic variable $PSScriptRoot contains the full path of the directory in which the executing script is located.

If you need your script to execute with its own directory as the working directory (current location), use the following approach:

# Save the current location and switch to this script's directory.
# Note: This shouldn't fail; if it did, it would indicate a
#       serious system-wide problem.
$prevPwd = $PWD; Set-Location -ErrorAction Stop -LiteralPath $PSScriptRoot

try {
 # Your script's body here.
 # ... 
 $PWD  # output the current location 
}
finally {
  # Restore the previous location.
  $prevPwd | Set-Location
}

Note:

  • The reason for explicitly restoring the previous location (directory) is that PowerShell runs scripts (.ps1 files) in-process, so if a script changes the current location with Set-Location or Push-Location, it takes effect session-globally; that is, the new location lingers even after the script exits.

  • On Unix-like platforms (Linux, macOS), with PowerShell Core, you now have the option of creating (extension-less) executable shell scripts with a shebang line; such scripts run in a child process, and there is therefore no need to restore the previous location (directory).

    • Unfortunately, accessing information about the script's own invocation, including $PSScriptRoot, is broken in shebang-based scripts, as of PowerShell Core 7.0.0-preview.4, as discussed in this GitHub issue.

[1] No module should ever change the session-global state on import (aside from importing its commands), but it is technically possible, namely if you place commands such as Set-Location in the top-level scope of a script module's *.psm1 file or in the scripts that run in the caller's scope via the ScriptsToProcess module-manifest entry. Even binary cmdlets can execute code at import time, via the System.Management.Automation.IModuleAssemblyInitializer interface - see this answer.

Share:
12,652
ZEE
Author by

ZEE

Just curious about life... ?what is it!!!

Updated on June 05, 2022

Comments

  • ZEE
    ZEE almost 2 years

    When I launch a PowerShell script from any folder (e.g. C:\Scripts), the 'current' PowerShell folder inside the script is always the User Profile Folder (e.g. c:\users\Joe) (test with a script with only Get-Location)

    But should be the folder from where the script was launched...

    how can I fix this?