Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating symbolic links or junctions with New-Item doesn't work if target contains '[' or ']' #6232

Open
lkydev opened this issue Feb 24, 2018 · 25 comments
Labels
Area-FileSystem-Provider specific to the FileSystem provider Issue-Bug Issue has been identified as a bug in the product KeepOpen The bot will ignore these and not auto-close Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module

Comments

@lkydev
Copy link

lkydev commented Feb 24, 2018

Steps to reproduce

  1. Type on an NTFS/ReFS file system:
New-Item -Path '[target]' -ItemType Directory
  1. Try creating a junction (or symbolic link, substitute with 'SymbolicLink' in the -ItemType parameter)
New-Item -Path '[source]' -Value '[target]' -ItemType Junction

Expected behavior

The junction should be created even if the target path contains square bracket characters

Actual behavior

An error message is resulted:

new-item : Cannot find path '[target]' because it does not exist.
At line:1 char:1
+ New-Item -Path '[source]' -Value '[target]' -Itemtype Junction
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [New-Item], PSInvalidOperationException
+ FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.NewItem Command

Note: the command can be made to work with double escapes, but it seems hacky.

new-item -path '[source]' -value "```[target```]" -Itemtype Junction

Environment data

Name                           Value
----                           -----
PSVersion                      6.0.1
PSEdition                      Core
GitCommitId                    v6.0.1
OS                             Microsoft Windows 6.1.7601 S
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
@iSazonov iSazonov added Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc. labels Feb 24, 2018
@iSazonov
Copy link
Collaborator

It is expected behavior because we support wildcards in paths.

Perhaps we could add -LiteralPath in New-Item

@lkydev
Copy link
Author

lkydev commented Feb 25, 2018

  1. I had the problem with the '-Value' parameter, not '-Path' or even '-LiteralPath'.

  2. So perhaps we need '-LiteralValue', not '-LiteralPath'.

@kwkam
Copy link
Contributor

kwkam commented Feb 26, 2018

-Name is -LiteralPath
This should work:

New-Item -Name '[target]' -ItemType Directory`
New-Item -Name '[source]' -Value '[target]' -ItemType Junction

@mklement0
Copy link
Contributor

mklement0 commented Feb 26, 2018

@iSazonov:

To be clear: wildcard support should not apply when creating new items.

NewItem's -Path parameter:

  • is, on the one hand, commendably already treated as a literal - the new item is created with the specified name as-is.

    • Similarly, -Name is treated literally, but invariably creates the new item in the current location.
  • on the other hand, the parameter name clashes with the use of -Path in cmdlets such as Get-ChildItem, where it implies wildcard support, in contrast with the -LiteralPath parameter;
    New-Item doesn't even have a -LiteralPath parameter (arguably, though, it should be added as an alias of -Path).

Similarly, the -Value parameter - whose alias name is -Target - should always treat a value passed to it as a literal, but currently doesn't - and that is the bug.

@kwkam: -Name is not an alias of -LiteralPath, not least because New-Item doesn't even have a -LiteralPath parameter (see above). Aside from that, the inappropriate treatment of a value passed to -Value / -Target as a wildcard expression is the same.

@iSazonov iSazonov added Issue-Bug Issue has been identified as a bug in the product and removed Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a labels Feb 26, 2018
@kwkam
Copy link
Contributor

kwkam commented Feb 26, 2018

Okay I forgot I was using a patched(#5896) build.

@mklement0 For consistency with other cmdlet's -Path parameter, and for the convenient with tab completion, I think -Path should stay as is. As for the -Path and -Name, what if we want to do, for example, New-Item -Path '`[path`]/item_[0-9]' -Name '[sub]/dir' -ItemType Directory? -Name can act as -LiteralPath in my opinion.

@mklement0
Copy link
Contributor

mklement0 commented Feb 26, 2018

@kwkam:

I think -Path should stay as is

I wasn't suggesting that New-Item's -Path be changed, I was suggesting that -LiteralPath be added as an alias for it.

The reason for suggesting that is that -Path already acts the way -LiteralPath usually does.

Conceptually speaking, -Name is a path component, and, more precisely, the leaf component - it should never accept a path.

Unfortunately, it currently does accept a path, namely a relative one: either relative to the path specified in -Path, or, in the latter's absence, relative to the current directory.

Aside from that, -Name doesn't - and shouldn't - accept wildcard patterns either.

For consistency, given that -Path and -Name don't accept wildcard patterns, neither should -Value (a.k.a. -Target).

@Moonsilence
Copy link

Similarly, the -Value parameter - whose alias name is -Target - should always treat a value passed to it as a literal, but currently doesn't - and that is the bug.

Hey! Googling an Error lead me here. Trying to create a Symlink to a path that contains square brackets using

New-Item -Path C:\my\path\cover.jpg -ItemType SymbolicLink -Value C:\my\path[1995]\cover.jpg

"not found"

Has this bug been fixed yet?

@vexx32
Copy link
Collaborator

vexx32 commented Jan 2, 2019

I see a lot of PRs with @kwkam's work but I'm not sure that specific issue has been completely fixed yet.

@anthonypants
Copy link

This is definitely still happening in 6.2.0.

@JessiAuro
Copy link

JessiAuro commented Aug 6, 2019

Running 6.2.2 on Linux and found this issue after hours of scratching my head. In my use case in particular it's important the original file names are preserved.

I tried escaping wildcard characters in the value I pass to -Value/-Target, but that just results in ItemNotFoundException. Re-reading the above, I've found that double-escaping the path first does solve the problem, but that's obviously not something that should be expected of anyone.

What's confusing me the most is from what I read I still don't quite understand why -Value isn't presumable literal. The discussion on the above PR seems much more concerned about -Path and doesn't seem to at all address that -Value should always be literal regardless of how -Path is handled.

@KaXaSA

This comment was marked as outdated.

@iSazonov
Copy link
Collaborator

I think this could be fixed with #13134

/cc @mklement0

@ghost
Copy link

ghost commented Mar 1, 2021

Meanwhile you can use [WildcardPattern]::Escape().

New-Item -ItemType SymbolicLink -Name "link.txt" -Target ([WildcardPattern]::Escape("C:\asdf [hello].txt")).

Meanwhile you can use [WildcardPattern]::Escape() if your target is already escaped:

New-Item -ItemType SymbolicLink -Name "link.txt" -Target ([WildcardPattern]::Escape("C:\asdf `[hello`].txt"))

@mklement0
Copy link
Contributor

Thanks, @OctaneTwisted, but this doesn't actually work:

  • The command seemingly succeeds,
  • but the resulting symlink is broken, because the escaped name is used verbatim as the target path.

@ghost
Copy link

ghost commented Apr 6, 2021

Thanks, @OctaneTwisted, but this doesn't actually work:

You're right, sorry. You actually need to escape it twice:
([WildcardPattern]::Escape([WildcardPattern]::Escape('C:\asdf [hello].txt')))

Or get tab completion to help you out when typing out the target path, since it gives you an escaped path ( 'asdf `[hello`]\test.txt' ), and just put that though Escape() once.

@mklement0
Copy link
Contributor

Thanks, @OctaneTwisted - good to know (and certainly unexpected; related: #7999).

Also worth noting that the workaround will break once the already green-lit proposal to treat a -Target argument literally gets implemented: see #13136 (comment)

@iSazonov iSazonov added Area-FileSystem-Provider specific to the FileSystem provider WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module and removed WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc. labels Nov 29, 2021
@C8H17OH
Copy link

C8H17OH commented Feb 10, 2022

Have same problem.

Three years past and still not solved...

Quite annoying for me, since I use BitTorrents to download files (whose names usually include '[', ']' and '&'), and use symbolic links to sync them between my disk and OneDrive.

I'm not familiar with PowerShell, but after reading this topic, as far as I'm concerned, I agree with @mklement0 that

-Target should always treat a value passed to it as a literal, but currently doesn't

...And it seems much easier to use old mklink in cmd, lol. Suck new techniques :)


By the way, for further reference, my current edition is PowerShell 7.2.1.

Name                           Value
----                           -----
PSVersion                      7.2.1
PSEdition                      Core
GitCommitId                    7.2.1
OS                             Microsoft Windows 10.0.22000
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

@yhm-amber
Copy link

yhm-amber commented Jan 20, 2023

Same issue:

image

image

code:

New-Item -Path '[X]' -Type SymbolicLink -Value '..\..\..\.kb\team\[X]'

So, I have to use mklink in CMD:

image

image

code:

mklink /D "[X]" "..\..\..\.kb\team\[X]"

@webtroter
Copy link

Hello!!

So I just hit this issue

And my kind Discord friend santisq asked me to test it with the double escapement.

That did the trick!

New-Item -ItemType Directory -Path C:\test
Set-Location C:\Test
New-Item -Path '.\`[brackets`] testing\' -Name '[brackets] test' -ItemType File -Force
new-item -ItemType HardLink -Path '.\link' -Value '.\`[brackets`] testing\`[brackets`] test'
new-item -ItemType HardLink -Path '.\link2' -Value ([WildcardPattern]::Escape([WildcardPattern]::Escape((Get-Item -LiteralPath '.\[brackets] testing\[brackets] test').FullName)))

@KaXaSA
Copy link

KaXaSA commented May 6, 2023

A quick script, to test a few of the suggestions mentioned here or in other pages.
So far I could not make it work when you have a situation where there is a subfolder with brackets.
Ex: C:\Test123\[Bracket]\Folder\file.txt

$base_path = 'C:\Test123'
$child_path = '[Bracket]\Folder\file.txt'

$file_path = Join-path -Path $base_path -ChildPath $child_path

New-Item -Path $file_path -Force

"
Testing Path: $file_path
Test-Path Result: $(Test-Path -LiteralPath $file_path)
"
 
 # * Array with the suggestions that will be used as the VALUE parameter in the New-Item command
 # ? You can add more items to the array, without changing anything else in this script
$value_array = @(
    $file_path
    $file_path -replace '(\[|\])' , '`$1'   # [Backet] -> `[Bracket`]
    $file_path -replace '(\[|\])' , '``$1'  # [Backet] -> ``[Bracket``]
    [WildcardPattern]::Escape((Get-Item -LiteralPath $file_path).FullName)
    [WildcardPattern]::Escape([WildcardPattern]::Escape((Get-Item -LiteralPath $file_path).FullName))
)

# ? Try to create a Hardlink using each item in the array as the value parameter
$n = 0
foreach ($value in $value_array) {
    $n++
    New-Item -ItemType HardLink -Path ( Join-path -Path $base_path -ChildPath "link$($n).txt" ) -Value $value
}

$PSVersionTable | Format-Table

Remove-Item -Path $base_path -Recurse -Confirm

My results (all fail)

    Directory: C:\Test123\[Bracket]\Folder

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          06/12/2023    09:58              0 file.txt

Testing Path: C:\Test123\[Bracket]\Folder\file.txt
Test-Path Result: True

New-Item:
Line |
  27 |      New-Item -ItemType HardLink -Path ( Join-path -Path $base_path -C …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot find path 'C:\Test123\[Bracket]\Folder\file.txt' because it does not exist.
New-Item:
Line |
  27 |      New-Item -ItemType HardLink -Path ( Join-path -Path $base_path -C …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Could not find item C:\Test123\`[Bracket`]\Folder\file.txt.
New-Item:
Line |
  27 |      New-Item -ItemType HardLink -Path ( Join-path -Path $base_path -C …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot find path 'C:\Test123\``[Bracket``]\Folder\file.txt' because it does not exist.
New-Item:
Line |
  27 |      New-Item -ItemType HardLink -Path ( Join-path -Path $base_path -C …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Could not find item C:\Test123\`[Bracket`]\Folder\file.txt.
New-Item:
Line |
  27 |      New-Item -ItemType HardLink -Path ( Join-path -Path $base_path -C …
     |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot find path 'C:\Test123\``[Bracket``]\Folder\file.txt' because it does not exist.


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

@sam-6174
Copy link

sam-6174 commented Jun 5, 2023

What worked for me is replacing New-Item with mklink:

cmd "/c" mklink "/h" "$link_file_path" "$src_file_path"

@mklement0
Copy link
Contributor

@sam-6174, yes, that's a viable workaround (previously mentioned); note that none of the double quotes are needed in your sample call. Also, since your example creates a hard link (/h) let me show symlink examples (cmd /c mklink /? shows help):

# Symlink to a FILE
cmd /c mklink    $link_file_path $src_file_path
# Symlink to a DIRECTORY
cmd /c mklink /D $link_file_path $src_file_path

@microsoft-github-policy-service microsoft-github-policy-service bot added the Resolution-No Activity Issue has had no activity for 6 months or more label Dec 2, 2023
@soredake
Copy link

soredake commented Dec 5, 2023

Still relevant.

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Resolution-No Activity Issue has had no activity for 6 months or more label Dec 5, 2023
@sam-6174
Copy link

sam-6174 commented Dec 6, 2023

@sam-6174, yes, that's a viable workaround (previously mentioned); note that none of the double quotes are needed in your sample call. Also, since your example creates a hard link (/h) let me show symlink examples (cmd /c mklink /? shows help):

# Symlink to a FILE
cmd /c mklink    $link_file_path $src_file_path
# Symlink to a DIRECTORY
cmd /c mklink /D $link_file_path $src_file_path

As it turns out, there's actually not enough quotes in the original code, i.e. this...

cmd "/c" mklink "/h" "$link_file_path" "$src_file_path"

... needs to be updated to this...

cmd "/c" mklink "/h" ('"' + $link_file_path + '"') ('"' + $src_file_path + '"')

Without these quotes, the original code will fail if $link_file_path contains an ampersand, i.e. &

@SteveL-MSFT SteveL-MSFT added KeepOpen The bot will ignore these and not auto-close Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors labels Dec 20, 2023
@SteveL-MSFT
Copy link
Member

In #13136 the PS-Committee made the decision that -Target should always be treated as literal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-FileSystem-Provider specific to the FileSystem provider Issue-Bug Issue has been identified as a bug in the product KeepOpen The bot will ignore these and not auto-close Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module
Projects
None yet
Development

No branches or pull requests