puravidaso
puravidaso

Reputation: 1233

How to find if a file exists under current directory under Powershell?

The question seems pretty easy, the method I used below should work but it doesn't:

PS C:\Users\John.Smith\Downloads> rm .\uucode.ps1
PS C:\Users\John.Smith\Downloads> [System.IO.File]::Exists("uucode.ps1")
True
PS C:\Users\John.Smith\Downloads> [System.IO.File]::Exists(".\uucode.ps1")
True

I deleted the file, but it still indicates the file exists. I figured out that it is looking for the file under my home directory (even when "." is specified):

PS C:\Users\John.Smith\Downloads> rm ..\uucode.ps1
PS C:\Users\John.Smith\Downloads> [System.IO.File]::Exists("uucode.ps1")
False
PS C:\Users\John.Smith\Downloads> [System.IO.File]::Exists(".\uucode.ps1")
False

Is this a bug or something? The OS and Powershell version I am using are:

PS C:\Users\John.Smith\Downloads> (Get-WmiObject -class Win32_OperatingSystem).Caption
Microsoft Windows Server 2012 R2 Standard

PS C:\Users\John.Smith\Downloads> $psversiontable.psversion
Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14409  1005

One solution I can think of myself is to find the current directory using pwd, and check if the file supplied to me is if a relative path (not starting with \ or /), and join the current directory with the relative path, but I think there should be an easier way, can you help me?

Upvotes: 0

Views: 1384

Answers (1)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 175065

Is this a bug or something?

No, this behavior is expected.

.NET methods that take relative paths as arguments will resolve them relative to the working directory of the calling process - and powershell.exe doesn't update its current working directory when you navigate between locations inside PowerShell itself:

PS C:\Users\John.Smith> [System.Environment]::CurrentDirectory
C:\Users\John.Smith
PS C:\Users\John.Smith> cd Downloads
PS C:\Users\John.Smith\Downloads> [System.Environment]::CurrentDirectory
C:\Users\John.Smith

You can solve it by only passing rooted file system paths:

PS C:\Users\John.Smith\Downloads> [System.IO.File]::Exists("$PWD\uucode.ps1")
PS C:\Users\John.Smith\Downloads> [System.IO.File]::Exists('C:\Users\John.Smith\Downloads\uucode.ps1')

... or, preferably, just stick to PowerShell's provider cmdlets - when in a FileSystem location, Test-Path -PathType Leaf would be the equivalent of [File]::Exists():

PS C:\Users\John.Smith\Downloads> Test-Path .\uucode.ps1 -PathType Leaf # always correctly resolves path relative to $PWD

If you want PowerShell to always update the current working directory of the host application process, you could do so in the prompt function

$function:prompt = {
  # Need to check that we're in the file system - can't set process directory to a registry path for example
  if ($PWD.Provider.Name -eq 'FileSystem') {
    [System.IO.Directory]::SetCurrentDirectory($PWD)
  }
  return "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
}

Upvotes: 3

Related Questions