Powershell: New-Item symlinks cannot be created if destination (target) is non-existent or not accessible

Created on 6 Mar 2019  Â·  8Comments  Â·  Source: PowerShell/PowerShell

Steps to reproduce

new-item -type symboliclink -name "hello" -target "Z:\"

Because I have to execute this as an administrator, my Z: drive is not attached, and so PowerShell responds with an error.

new-item : Cannot find drive. A drive with the name 'Z' does not exist.
At line:1 char:1
+ new-item -type symboliclink -name "hello" -target "Z:\"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (Z:String) [New-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.NewItemCommand

It appears this was thought to be fixed in #801, but evidently it didn't include non-existent drives.

Instead I have to resort to CMD MKLINK.

cmd
> mklink /D "hello" "Z:\"
> exit

Environment data

Name                           Value
----                           -----
PSVersion                      6.2.0-rc.1
PSEdition                      Core
GitCommitId                    6.2.0-rc.1
OS                             Microsoft Windows 10.0.17763
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Edit 1: fixed spellings, changed parameter to '-target'.

Issue-Question WG-Engine-Providers

All 8 comments

I think this issue might have to do with symbolic links need to know if it is a directory or a file based symlink. MKLINK requires specifying the purpose of the symbolic link. PowerShell is probably trying to find the target so that it can determine the type of link from the target. That doesn't work when the target cannot be accessed at that time.

I also tested creating a symbolic link to a non-existent server via a UNC path and got the same results, except that it could not find 'the network path', so I don't think it has to do with drives as I originally though.

This might require new enumerations for -itemtype (type) to bypass the target lookup. I imagine this is not required for unix, and so this is probably a Windows only issue.

2915 seems to have a good read on this … but I am not sure what happened along the way.

This is what happened (quoting @joeyaiello):

Well, it looks like your PR (#3509) is doing the right thing for now, If people desperately need directory symlinks to non-existent targets, we'll figure it out then.

The status quo:

  • You _can_ create symlinks to nonexistent targets, except if the target path starts with a nonexistent drive / UNC share, which prompted creation of _this_ issue, and which is a _bug_ that should be fixed.

  • When creation of a symlink with a nonexistent target succeeds, currently a _file_ symlink is invariably created - in the absence of being able to _infer_ whether the target is a file or a directory.

As you state, a proper fix requires _some_ way to explicitly indicate the type of a nonexistent target (and, indeed, it wouldn't be required on Unix-like platforms).

Building on your idea to introduce new -ItemType enumeration values, we could do the following:

  • Introduce FileSymlink and DirectorySymlink values in addition to Symlink.

    • If FileSymlink or DirectorySymlink are used and the target exists but doesn't match the requested type, report an error.
  • Additionally, given that _directory_ symlinks are far more common than file symlinks, we could change the default (if only Symlink is used) to a directory symlink on Windows - though that is technically a breaking change.

Some thoughts:

  • Maybe instead of adding ItemType enumerations, it could allow an array of them, so that one could specify -itemtype directory, symboliclink to enforce the directory symbolic link. This would require new new enumerations, but would require changing the definition of the parameter, and also handling the otherwise limited use of an array parameter.
  • If the type of link has been suggested, but PowerShell still insists on checking to see if it is the right choice (MKLINK does not care) there will be a delay with non-existent or unreachable UNC paths, and possibly with others if the media is offline at the moment.
    -Ppreviously I had found it more common to make symlinks for directories, but recently I have been relying on them to symlink files for the purpose of replacing VS Code's default grammar/syntax files with my own custom ones, as I am not ready to figure out how to replace them with an extension that could break other desired functionality. I just have to replace the symlinks every time VS Code updates.

Good points, @msftrncs.

  • Not performing any existence or type-consistency check is an option, though I wish there were an opt-in/opt-out mechanism:

    • Arguably, an existence/consistency check should be performed _by default_, with a switch allowing opt-out, but that ship has sailed.
    • We could offer an opt-in, such as with a -Verify switch.
  • As for making -ItemType array-valued: that's also an option, though I worry that it is both less discoverable and more cumbersome; it also requires more work behind the scenes to rule out nonsensical combinations.

  • Point taken, re file symlinks - no compelling reason to change the default.

Hi.
When we create a symlink, is it possible that the lastwrite is the same as the original file? If yes how to do?

Thank you for your answer.

Best regards

Jean-Marc

When we create a symlink, is it possible that the lastwrite is the same as the original file? If yes how to do?

When you create a symbolic link to a unicorn, you cannot make a note of when said unicorn recently pooped.

@jmg69, that's not how the filesystem works; symlinks have their own last-write timestamps (which never change, unless you recreate the symlink).

Therefore, you must manually determine the target file (assuming it exists) and then query its properties:

$symlinkPath = './foo'
Get-Item (Get-Item $symlinkPath).Target # use (...).LastWriteTime to get just the last-write time

If a given path may or may not be a symlink:

$potentialSymlinkPath = './foo'
($item = Get-Item $potentialSymlinkPath).Target ? (Get-Item $item) : $item

But please note that this issue is about symlinks with _non-existent_ targets.
If you have further questions, I suggest you use the following community resources:

Was this page helpful?
0 / 5 - 0 ratings