3

Quite simply, I need to check if a file exists in the user's home drive using PowerShell. This script will be executed on a fleet of machines so the path needs to be relative.

Current output:

# Create file named 'foo' in home dir
New-Item '~/foo'

# Check if the file exists
[System.IO.File]::Exists('~/foo')
# Returns false

Listing the file shows it definitely exists:

ls '~/foo'

Directory: C:\Users\tom_n


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----           20/02/2018 14:13              0 foo

Am I missing something obvious here? I also tested this with a file that has an actual size, also to no avail.

I'm appreciating any input

1
  • 4
    try it with Test-Path (example: Test-Path ~\foo.txt) Commented Feb 20, 2018 at 14:26

3 Answers 3

3

tl;dr:

Don't use ~ - use $HOME to refer to the current user's home directory, typically inside "...":

[System.IO.File]::Exists("$HOME/foo")

Or, preferably, use PowerShell's Test-Path cmdlet:

Test-Path -LiteralPath "$HOME/foo"

Note:
* PowerShell and .NET types accept / and \ interchangeably as the path separators; with a view toward potential cross-platform compatibilty, choose /.
* Strictly speaking, since "$HOME/foo" is passed to Test-Path in argument mode (command-line style), enclosing in "..." isn't necessary in this case (try Write-Output $HOME/foo), but enclosing in "..." is a good habit to form, because it works in a wider range of scenarios.


PowerShell's ~ is not fully equivalent to ~ on Unix (in POSIX-like shells) and may not work the way you expect it to in PowerShell:

As explained in this TechNet article[1], ~ in PowerShell refers to the home location as defined by the current location's drive provider, which:

  • may or may not be the filesystem.
  • may or may not be defined.

On Windows, consider the following example, which uses the registry drive provider:

Set-location HKCU:\Software
Set-Location ~

which yields the following error:

Set-Location : Home location for this provider is not set. To set the home location, call "(get-psprovider 'Registry').Home = 'path'".
...

As you can see, because the current location was on a drive of the registry provider, ~ was interpreted as that provider's idea of the home location, which, however, happens not to be defined.

An additional crucial difference:

  • On Unix, ~ is a shell feature: it must be used unquoted, in which case it is expanded to the full, literal home-directory by the shell, before the target command sees the path, so that the target command sees a literal path and doesn't need to know about ~, and, in fact, the standard utilities do not know about ~, which you can verify by contrasting ls ~ (OK) with ls '~' (tries to list a file/dir literally named ~).

  • In PowerShell, ~ is a PowerShell drive-provider feature: ~ is passed as-is to drive-provider cmdlets such as Get-ChildItem and they interpret ~ as referring to the current drive's home location. External utilities (e.g., findstr.exe on Windows) and the .NET Framework do not follow this convention and therefore interpret the ~ as a literal filename.


By contrast, automatic variable $HOME is the PowerShell equivalent of Unix ~, with added flexibility:

While Unix ~ has to be unquoted in order to expand to the user's home directory, PowerShell's automatic $HOME variable can be referenced inside double-quoted strings as well (as part of normal string expansion (interpolation)).


Finally, .NET types such as [System.IO.File] themselves support neither ~ nor $HOME, but by using "$HOME/..." it is PowerShell that ensures that $HOME is replaced with the actual, literal home-directory path before the path string is passed to a .NET method.


[1] Get-Help about_Locations and Get-Help about_Path_Syntax, the official help topics on the subject, should contain information about ~, but as of this writing do not.

Sign up to request clarification or add additional context in comments.

1 Comment

This should be the god answer.
2

The PowerShell CmdLet for that is Test-Path :

Test-Path '~/foo'

You can use the Windows file represention with .NET classes

[System.IO.File]::Exists('.\foo')

3 Comments

Just be aware that .\Foo may or may not be the same path as ~/Foo. It will only be the same path if your pwd happens to be your home dir.
Getting a command line that was more *nix-like was one of the original goals for Powershell.
Good point about .\foo, @EBGreen. Note that ~ may refer to a location that is not a filesystem location, as it is relative to the PS drive provider underlying the current location; $HOME, by contrast, always refers to user's home directory in the filesystem.
1

I think the problem is that the Exists() does not resolve the home dir specified with ~. Try using relative or absolute path

3 Comments

Why the downvote? My statement is correct. You can check with: [System.IO.Path]::GetFullPath('~/asd') That returns 'currentdir/~/asd'. The ~ is not resolved.
I can only guess but the two things I would change is see if you have find anything authoritative as starting with I think might not be doing you favours. Also your comment actually adds value as a precendent for how those methods work. I would put that into your answer.
Also, also, if the OP is using ~ in the *nix sense, present working dir is only the right answer if your present working dir happens to be your home dir. It would be wrong in any other dir.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.