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

Created on 24 Feb 2018  路  13Comments  路  Source: PowerShell/PowerShell

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
Issue-Bug WG-Engine-Providers

Most helpful comment

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.

All 13 comments

It is expected behavior because we support wildcards in paths.

Perhaps we could add -LiteralPath in New-Item

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

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

-Name is -LiteralPath
This should work:

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

@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.

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.

@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).

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:mypathcover.jpg -ItemType SymbolicLink -Value C:mypath[1995]cover.jpg

"not found"

Has this bug been fixed yet?

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

This is definitely still happening in 6.2.0.

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.

So I ran some tests because I was having issues with the [square brackets] inside New-Item ... HardLink -Value here are the results:

[Powershell 7.0.3]

Test 1 | -Value with DOUBLE backticks
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\``[subfolder A``]\file.txt' -Force
[Done] Hardlink created: C:\bfolder001\file.txt
#####

Test 2 | -Value with DOUBLE backticks + SUBFOLDER(B)
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\``[subfolder A``]\subfolder B\file.txt' -Force
[Error] Cannot find path 'C:\bfolder001\``[subfolder A``]\subfolder B\file.txt' because it does not exist.
#####

Test 3 | -Value with SINGLE backticks
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\`[subfolder A`]\file.txt' -Force
[Error] Could not find item C:\bfolder001\`[subfolder A`]\file.txt.
#####

Test 4 | -Value with SINGLE backticks + SUBFOLDER(B)
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\`[subfolder A`]\subfolder B\file.txt' -Force
[Error] Could not find item C:\bfolder001\`[subfolder A`]\subfolder B\file.txt.
#####

Name                           Value
----                           -----
PSVersion                      7.0.3
PSEdition                      Core
GitCommitId                    7.0.3
OS                             Microsoft Windows 10.0.19041
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

[Powershell 5.1]

Test 1 | -Value with DOUBLE backticks
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\``[subfolder A``]\file.txt' -Force
[Done] Hardlink created: C:\bfolder001\file.txt
#####

Test 2 | -Value with DOUBLE backticks + SUBFOLDER(B)
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\``[subfolder A``]\subfolder B\file.txt' -Force
[Error] Cannot find path 'C:\bfolder001\``[subfolder A``]\subfolder B\file.txt' because it does not exist.
#####

Test 3 | -Value with SINGLE backticks
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\`[subfolder A`]\file.txt' -Force
[Done] Hardlink created: C:\bfolder001\file.txt
#####

Test 4 | -Value with SINGLE backticks + SUBFOLDER(B)
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\`[subfolder A`]\subfolder B\file.txt' -Force
[Done] Hardlink created: C:\bfolder001\file.txt
#####

Name                           Value
----                           -----
PSVersion                      5.1.19041.1
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.1
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Can someone explain why in PowerShell 7 the New-Item works in Test 1 but doesn't work in Test 2?

Test 1 | -Value with DOUBLE backticks
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\``[subfolder A``]\file.txt' -Force
[Done] Hardlink created: C:\bfolder001\file.txt
#####

Test 2 | -Value with DOUBLE backticks + SUBFOLDER(B)
Command: New-Item -Path 'C:\bfolder001\file.txt' -ItemType HardLink -Value 'C:\bfolder001\``[subfolder A``]\subfolder B\file.txt' -Force
[Error] Cannot find path 'C:\bfolder001\``[subfolder A``]\subfolder B\file.txt' because it does not exist.
#####

AFAIK the only difference here is the extra subfolder, I changed this:
-Value 'C:\bfolder001\``[subfolder A``]\file.txt'

To:
-Value 'C:\bfolder001\``[subfolder A``]\subfolder B\file.txt'


The script if you want to test/check to see if I did something wrong:

$bFolder = 'C:\bfolder001'
$subfA  = '[subfolder A]'
$subfB = 'subfolder B'
$txtFile = 'file.txt'

$backtickS = '`$1'
$backtickD = '``$1'

function Test {
    param (
        [String]$bFolder, [String]$subfA, [String]$subfB, [String]$txtFile, [String]$backtick
    )

    $fullName = "$bFolder\$subfA\$subfB\$txtFile" -replace '\\{2,}', '\' # replace \\ (2 or more) with only one \
    $fullNameEsc = "$fullName" -replace "(\]|\[)", "$backtick" # replace [] with `[`] OR ``[``]
    $hardlinkT = "$bFolder\$txtFile" -replace '\\{2,}', '\'

    Try {
        New-Item -Path $fullName -ItemType File -Force -ErrorAction Stop | out-null

        "Command: New-Item -Path `'$hardlinkT`' -ItemType HardLink -Value `'$fullNameEsc`' -Force" | Write-Host -ForegroundColor Cyan
        New-Item -Path $hardlinkT -ItemType HardLink -Value $fullNameEsc -Force -ErrorAction Stop | out-null
        "[Done] Hardlink created: $bFolder\$txtFile" | Write-Host -ForegroundColor Green
    } Catch {
        "[Error] $_" | Write-Host -ForegroundColor Red
    } Finally {
        if ($bFolder -notmatch "^\w:\\?$") {
            Remove-Item -Path $bFolder -Recurse #-Confirm
        }
        "#####" | Write-Host -ForegroundColor Gray
    }
} #end function

"`nTest 1 | -Value with DOUBLE backticks" | Write-Host
Test $bFolder $subfA $null $txtFile $backtickD

"`nTest 2 | -Value with DOUBLE backticks + SUBFOLDER(B)" | Write-Host
Test $bFolder $subfA $subfB $txtFile $backtickD

"`nTest 3 | -Value with SINGLE backticks" | Write-Host
Test $bFolder $subfA $null $txtFile $backtickS

"`nTest 4 | -Value with SINGLE backticks + SUBFOLDER(B)" | Write-Host
Test $bFolder $subfA $subfB $txtFile $backtickS 


$PSVersionTable
pause

I guess this is related to https://github.com/PowerShell/PowerShell/issues/13136

I think this could be fixed with #13134

/cc @mklement0

if Install the preview (v7.1.0-rc.1) does it fix the problem? or that wasn't implemented yet? (I will probably test it later)

Was this page helpful?
0 / 5 - 0 ratings