Reputation: 3962
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
Upvotes: 3
Views: 844
Reputation: 440162
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:
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.
Upvotes: 3
Reputation: 1835
I think the problem is that the Exists()
does not resolve the home dir specified with ~
. Try using relative or absolute path
Upvotes: 1
Reputation: 72680
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')
Upvotes: 2