Powershell: Move-Item ignores directories which have name startwith bracket

Created on 11 Jul 2020  ·  5Comments  ·  Source: PowerShell/PowerShell

Steps to reproduce

  1. create a directory which has name starts with bracket (eg. "[1]powershell")
  2. try rename using mv or Move-Item the directory

Expected behavior

PS sandbox ❯ mkdir "[1]powershell"


    Directory: C:\Users\hdformat\dev\sandbox

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----        2020-07-11 오후 10:50                [1]powershell

PS sandbox ❯ Move-Item "[1]powershell" "[1]PowerShell"

PS sandbox ❯ ls


    Directory: C:\Users\hdformat\dev\sandbox

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----        2020-07-11 오후 10:50                [1]PowerShell

PS sandbox ❯

Actual behavior

PS sandbox ❯ mkdir "[1]powershell"


    Directory: C:\Users\hdformat\dev\sandbox

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----        2020-07-11 오후 10:50                [1]powershell

PS sandbox ❯ Move-Item "[1]powershell" "[1]PowerShell"

PS sandbox ❯ ls


    Directory: C:\Users\hdformat\dev\sandbox

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----        2020-07-11 오후 10:50                [1]powershell

PS sandbox ❯

Environment data

PS sandbox ❯ $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.0.2
PSEdition                      Core
GitCommitId                    7.0.2
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

PS sandbox> get-module

ModuleType Version    PreRelease Name                                ExportedCommands
---------- -------    ---------- ----                                ----------------
Manifest   7.0.0.0               Microsoft.PowerShell.Management     {Add-Content, Clear-Content, Clear-Item, Clear-It…
Manifest   7.0.0.0               Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Ob…
Script     2.0.412               oh-my-posh                          {Get-ComputerName, Get-FormattedRootLocation, Get…
Script     0.7.3                 posh-git                            {Add-PoshGitToProfile, Add-SshKey, Enable-GitColo…
Script     2.0.0                 PSReadLine                          {Get-PSReadLineKeyHandler, Get-PSReadLineOption, …
Issue-Question Resolution-Answered

All 5 comments

Since you're passing the source path _positionally_, it binds to the -Path parameter, which interprets it as a _wildcard expression_ (glob).

As such, it would match a file literally named 1powershell (or case variations therefore) but not one named [1]powershell.

If there's no 1powershell file, which is probably the case for you, the call is an effective no-op (no moving / renaming occurs).

The correct approach is to use the -LiteralPath parameter:

Move-Item -LiteralPath "[1]powershell" "[1]PowerShell"

As an aside: Note that PowerShell has a dedicated Rename-Item cmdlet, which makes it easier to rename items in place, irrespective of their location.

@mklement0 Thanks for the explanation,
I didn't know about the -LiteralPath option. However, it feels awkward to have to use widely used file system commands differently in PowerShell, not only mv(Move-Item) but cd (change directory)

PS sandbox ❯ mkdir [1]powershell

    Directory: C:\Users\hdformat\dev\sandbox

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----        2020-07-13  오전 3:03                [1]powershell

hdformat at hdformat-pc 🐍 ~\venv                                                                       [30 ms][03:03]
PS sandbox ❯ cd [1]powershell
Set-Location: Cannot find path '[1]powershell' because it does not exist.

⨯ hdformat at hdformat-pc 🐍 ~\venv                                                                     [68 ms][03:03]
PS sandbox ❯ cd "[1]powershell"
Set-Location: Cannot find path '[1]powershell' because it does not exist.

⨯ hdformat at hdformat-pc 🐍 ~\venv                                                                     [29 ms][03:03]
PS sandbox ❯ cd -LiteralPath "[1]powershell"
hdformat at hdformat-pc 🐍 ~\venv                                                                        [7 ms][03:03]
PS [1]powershell ❯

I agree that it's awkward, but this behavior can't be changed without introducing new syntax.

While POSIX-like shells use unquoted vs. quoted to distinguish between wildcards (globs) and literals (* vs, '*', for instance, PowerShell has no such distinction - get-item * is the same as get-item '*' - which both bind to the -Path parameter.

Given that filenames with * and ? are very rare, the problem is usually restricted to [ and ].

To ease the pain, you can use -lp in lieu of -LiteralPath.

```powershell
cd -lp [1]powershell
````

Alternatively, you can `-escape [ and ], but there's a syntax pitfall: if the argument is _unquoted_ or inside "..." (these two forms are in essence equivalent, you need _2_ ` instances (because ` also acts as the escape character in double-quoted strings); inside '...' (a verbatim string), a single ` is sufficient.

```powershell

The following commands are equivalent

cd [1]powershell
cd "[1]powershell"

cd '[1]powershell'
````

To be clear: this issue is a long-standing pain point and solutions have been discussed in various issues here; for instance, I've indeed proposed a new syntax here: https://github.com/PowerShell/PowerShell/issues/9308#issuecomment-491023897

This issue has been marked as answered and has not had any activity for 1 day. It has been closed for housekeeping purposes.

Was this page helpful?
0 / 5 - 0 ratings