Copy the file with square brackets [] in the file name and use * wildcard

I use PowerShell on Windows 7 and write a script to copy a bunch of files from one folder structure to another. This is like compilation. The PowerShell Copy-Item cmdlet considers that the square brackets, [], are wildcards of some type, and for some reason I cannot escape them.

I cannot use -LiteralPath because I want to use the * wildcard, since the file name has a date as part of the file name, and the date changes. Date is used as version number.

This post was useful, but the number of ticks (2x or 4x per bracket) does not go beyond the square brackets.

I do not get an error; PowerShell behaves as if I entered the wrong file name.

This is the specific line I'm working on:

 #to Fusion Server Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\"[RoomView] Versions explained*.pdf" -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\ 

And it's all:

 # Compiles the Fusion packet for distribution ############################### ###########Variables########### ############################### #folder structure $FSG = "F:\FSG" $containerFolder = "Packet.Fusion for IT and AV Professionals" $rootFolder = "Fusion for IT and AV pros $(Get-Date -format "MM-dd-yyyy")" $subRoot1 = "Fusion Server" $subRoot2 = "Scheduling Enhancement and Panels" $subRoot2sub1 = "Scheduling Panels" $subRoot3 = "SQL Server" #source folders $HW = "0.Hardware" $3SMDoc = "0.Hardware\TPMC-3SM.Documentation" $4SMDoc = "0.Hardware\TPMC-4SM.Documentation" $4SMDDoc = "0.Hardware\TPMC-4SM-FD.Documentation" $730Doc = "0.Hardware\TSW-730.Documentation" $730OLH = "0.Hardware\TSW-730.OLH" $CENRVS = "0.Hardware\CEN-RVS.Notes" $ProjMgmt = "0.Project Management" $SW = "0.Software" $RVLicensing = "0.Software\0.RoomView.License" $RVNotes = "0.Software\0.RoomView.Notes" $SQLLicensing = "0.Software\database.SQL.Licensing" $SQLNotes = "0.Software\database.SQL.Notes" $FRVMarketing = "0.Software\Fusion RV.Marketing" $FRVNetworking = "0.Software\Fusion RV.Networking" $FRVNotes = "0.Software\Fusion RV.Notes" ############################### #create the directory structure ############################### md -Path $FSG\$containerFolder -Name $rootFolder cd $FSG\$containerFolder\$rootFolder md "eControl and xPanels" md "Fusion Server" #$subRoot1 md "Getting Started as a User" md "Project Management" md "RoomView Connected Displays" md "Scheduling Enhancement and Panels" #$subRoot2 md "SQL Server" #$subRoot3 cd $FSG\$containerFolder\$rootFolder\$subRoot1 md "CEN-RVS" md "Licenseing Information" md "Networking" md "Official Documentation" md "Prerequisites, including powerShell script" md "Product Info" md "Requirements" md "Tech Info" md "Windows Authentication to Fusion RV" cd $FSG\$containerFolder\$rootFolder\$subRoot2 md "Outlook Add-in" md "Scheduling Panels" #$subRoot2sub1 cd $FSG\$containerFolder\$rootFolder\$subRoot2\$subRoot2sub1 md "TPMC-3SM" md "TPMC-4SM" md "TPMC-4SM-FD" md "TSW-730" cd $FSG\$containerFolder\$rootFolder\$subRoot3 md "Multi-database model only" md "SQL Licensing" cd $FSG\$containerFolder #reset current folder ############################### #copy the files ############################### #Copy-Item -Path C:\fso\20110314.log -Destination c:\fsox\mylog.log #To the root Copy-item -Path $FSG\$ProjMgmt\starter\"Fusion Support Group Contact info*.pdf" -Destination $FSG\$containerFolder\$rootFolder\ Copy-item -Path $FSG\$containerFolder\"Fusion for IT and AV professionals release notes.txt" -Destination $FSG\$containerFolder\$rootFolder\ #to eControl and xPanels Copy-item -Path $FSG\$SW\xpanel.Notes\starter\*.* -Destination $FSG\$containerFolder\$rootFolder\"eControl and xPanels"\ #to Fusion Server Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\"[RoomView] Versions explained*.pdf" -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\ 

What can I do to avoid the square brackets and still use part of the subdirectory name in the Copy-Item cmdlet?

+12
powershell copy square-bracket
source share
9 answers

There is a difference between ' and ' :

  • The first is a single quote, which is not a shift symbol on the key. "
  • The second is a kickback, which I thought I used, but actually was not. This is a character without an offset on the ~ key.

It works:

 # to Fusion Server Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\'''[RoomView''] Versions explained*.pdf' -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\ 
0
source share

In this situation, you should use double backticks with single quotes to avoid parentheses. You can also use quadruple backticks when using strings in double quotes.

Thus, a fixed line of code:

 Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\'''[RoomView''] Versions explained*.pdf' -Destination $FSG\$containerFolder\$rootFolder\'Fusion Server'\ 

Another good source of information about file paths, wired characters, etc. - read this article: Taking things (like file paths) literally


EDIT

Thanks @ mklement0 for emphasizing that the true reason for this mismatch is due to an error currently in PowerShell 1 . This error escapes wildcards, as well as quotes with the -Path option, behave differently by default than other options, such as the -Include and -Filter .

To expand on @ mklement0 excellent answer , and comments , and other answers below:

To better understand why we need single quotes and double-ticks in this situation ; ( and to highlight the bug bug and inconsistencies), let's look at a few examples to demonstrate what happens:

Get-Item and the associated cmdlets ( Get-ChildItem , Copy-Item , etc.) handle the -Path parameter differently when dealing with a combination of escaped characters , escaped characters, and escaped wildcards * at the same time *** !

TLDR The main reason we need a combination of single quotes and double backticks is because the underlying PowerShell provider parses the -Path parameter string for wildcards. It looks like it is parsed once for escape characters and a second time for wildcard evaluation.

Let's look at a few examples to demonstrate this strange result:

First, let's create two files for testing with the names File[1]a.txt and File[1]b.txt

 "MyFile" | Set-Content '.\File'[1']a.txt' "MyFriend" | Set-Content '.\File'[1']b.txt' 

We will try different ways to get the file. We know that the square brackets [ ] are wildcards wildcards , so we need to escape them with the backtick character .

We will try to get one file explicitly.

Let's start by using single quotes for letter strings:

 PS C:\> Get-Item 'File[1]a.txt' PS C:\> Get-Item 'File'[1']a.txt' Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2019-09-06 5:42 PM 8 File[1]a.txt PS C:\> Get-Item 'File''[1'']a.txt' Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2019-09-06 5:42 PM 8 File[1]a.txt 

For strings in single quotes, all that is required to extract the file is a single backquote, but two backquotes also work.

Using strings in double quotes, we get:

 PS C:\> Get-Item "File[1]a.txt" PS C:\> Get-Item "File'[1']a.txt" PS C:\> Get-Item "File''[1'']a.txt" Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2019-09-06 5:42 PM 8 File[1]a.txt 

For double-quoted strings, as expected, we see that we need two backslashes for this to work.

Now we want to get both files , and , use a wildcard.

Let's start with single quotes:

 PS C:\> Get-Item 'File[1]*.txt' PS C:\> Get-Item 'File'[1']*.txt' PS C:\> Get-Item 'File''[1'']*.txt' Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2019-09-06 5:42 PM 8 File[1]a.txt -a---- 2019-09-06 5:49 PM 10 File[1]b.txt 

In single quotes, when we have a wildcard character, we need two sets of backticks. One to avoid the brackets, and a second backslash to avoid the backslash, which we used to avoid the brackets when the wildcard is calculated.

Similarly for double quotes:

 PS C:\> Get-Item "File[1]*.txt" PS C:\> Get-Item "File'[1']*.txt" PS C:\> Get-Item "File''[1'']*.txt" PS C:\> Get-Item "File'''[1''']*.txt" PS C:\> Get-Item "File''''[1'''']*.txt" Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2019-09-06 5:42 PM 8 File[1]a.txt -a---- 2019-09-06 5:49 PM 10 File[1]b.txt 

With double quotes, evaluate in more detail with a wildcard. In this case, we need four sets of back ticks. For double quotes, we need two back quotes to go beyond the brackets, and two more back quotes to escape the escape characters when it comes to evaluating a star wildcard.


EDIT

As @ mklement0 mentions , this behavior with the -Path parameter is inconsistent and behaves differently than the -Include parameter, where only one -Include to correctly exit the brackets. This may be โ€œfixedโ€ in a later version of PowerShell.


1 As of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.3

+19
source share

The way that PowerShell automatically adds a file name is usually the best way.

Example:

 copy-item '.\file`[test`].txt' 
+3
source share

In PowerShell version 2.0 and above, the escape character uses a backslash. For example, if we want to remove the brackets from this line "[Servername: QA01]", which is the type of output that we get from the Exchange PowerShell cmdlet in System Center Orchestrator, we use the following logic:

 $string -replace '\[','' -replace '\]','' >Servername: QA01 

This is pretty weird. See, you should use a single quote (which usually means in PowerShell "evaluate it exactly as it is written," so this is a very strange syntax).

Do not feel bad because you do not understand it yourself, this is a very strange syntax.

+3
source share

It seems square brackets need double backward steps to avoid, which is unusual. Link here .

Are you sure this won't work? I have seen it mentioned several times.

Edit: Yes, this works, you used double quotes instead of backlinks.

A double quote is located above the apostrophe symbol, next to the Enter key. The backtick is located directly under the Escape key, dividing the key with the tilde, ~.

+1
source share

I use this:

  Copy-Item $file.fullname.replace("[", "''[").replace("]", "'']") $DestDir 
+1
source share

review and some background information :

  • To effectively escape the character that you want to be interpreted verbatim as part of the wildcard , it must be ' -escaped, as the target cmdlet sees it (its base PowerShell drive). provider).

  • This can be tricky because the ' (backtick) is also used as an escape character in double-quoted strings ( "..." ) and unquoted command arguments (which for the most part behave like double-quoted strings).

Note. The script in question does not allow the use of -LiteralPath , but in cases where you know that the path is a specific, literal path, use -LiteralPath (which can be shortened to
-lp in PowerShell Core) is the best choice - see this answer .

When passing an argument to the -Path parameter, which supports wildcards, the cmdlet associated with the PowerShell drive provider ( Get-ChildItem , Copy-Item , Get-Content , ...) and is required [ and ] should be considered verbatim and not as a character set / range expression:

  • String and letter representations :

    • 'file'[1'].txt'

      • ' Char. stored as it is inside '...' , so the target cmdlet sees them as expected.
    • "file''[1''].txt"

      • '' , i.e. inside "..." , doubling is necessary to save one ' in the resulting string (the first ' is the escape character (double quotes) of the inner string, and the second ' is the character that he escapes to go through it.
    • file''[1''].txt

      • The same goes for command arguments without quotes, which (for the most part) act as "..."
    • Warning : Due to an error - see this issue on GitHub - mixing (no exit) ? or * escaped [ and ] requires the latter to be escaped twice (with '' , as the target cmdlet / provider sees) :

      • If you want to match the literal file name file[1].txt with a wildcard that matches literally [ and ] and also contains the special character * (to match any character set), instead of the expected 'file'[1']*' , you have to use 'file''[1'']*' (sic); with an argument in double quotes or without escaping, you should effectively use the four-fold back commas: "file''''[1'''']*" / file''''[1'''']* - see this answer for more information.

      • Note that the direct use of wildcards with the -like operator is not affected:

        • 'a[b' -like 'a'[*' - this is correct - $true ,
        • whereas 'a[b' -like 'a''[*' - rightfully - complains about the wrong template.
      • Similarly, the -Include and -Exclude not affected .

      • -Filter played according to other rules for starters: [...] as a construct, it is not supported at all, but the characters [ and ] . always considered literals (see this answer ).

  • To escape a path string programmatically through a variable use:

     $literalName = 'file[1].txt' $escapedName = [WildcardPattern]::Escape($literalName) # -> 'file'[1'].txt' 

+1
source share

One option is to get the file names using an outdated directory that allows you to use the wildcard character *, but does not try to โ€œcolorโ€ the square brackets. Then load this list to move the item using -literalpath

  cmd /c dir *]* /b | foreach { Move-Item -LiteralPath $_ -Destination <destination path> } 
0
source share

Assuming nothing else fits, can you use? instead of brackets. A file named "a [hj]" copied to the "foo" directory:

  copy-item a?hj? foo 
0
source share

All Articles