Exclude list in PowerShell Copy-Item does not appear to be working

前端 未结 10 920
旧时难觅i
旧时难觅i 2020-12-02 11:40

I have the following snippet of PowerShell script:

$source = \'d:\\t1\\*\'
$dest = \'d:\\t2\'
$exclude = @(\'*.pdb\',\'*.config\')
Copy-Item $source $dest -R         


        
10条回答
  •  长情又很酷
    2020-12-02 12:41

    Note that the syntax spec calls for a STRING ARRAY; ala String[]

    SYNTAX
        Copy-Item [[-Destination] ] [-Confirm] [-Container] [-Credential ] [-Exclude ] [-Filter ] [-Force] [-FromSession ] [-Include 
        ] -LiteralPath  [-PassThru] [-Recurse] [-ToSession ] [-UseTransaction] [-WhatIf] []
    

    If you're not explicit in your array generation, you end up with an Object[] - and that is ignored in many cases, leaving the appearance of "buggy behavior" because of type-safety. Since PowerShell can process script-blocks, evaluation of other than a type-specific variable (so that a valid string could be determined) would leave an opening for the potential of an injection mode attack on any system whose execution policy were lax.

    So this is unreliable:

    PS > $omissions = @("*.iso","*.pdf","*.zip","*.msi")
    PS > $omissions.GetType()
    
    Note the result....
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     Object[]                                 System.Array
    

    And this works.... for example:

    PS > $omissions = [string[]]@("*.iso","*.pdf","*.zip","*.msi")
    **or**
    PS > [string[]]$omissions = ("*.iso,*.pdf,*.zip,*.msi").split(',')
    PS > $omissions.GetType()
    
    IsPublic IsSerial Name                                     BaseType
    -------- -------- ----                                     --------
    True     True     String[]                                 System.Array
    

    Note that even a "single" element would still require the same cast, so as to create a 1-element array.

    If you're trying this at home, be sure to use the Replace-Variable "omissions" to clean out the existence of $omissions before recasting it in the examples shown above.

    And as far as a pipeline that works reliably that I've tested....

    --------------------------------------------------------------------------------------- cd $sourcelocation
    
    ls | ?{$_ -ne $null} | ?{$_.BaseName -notmatch "^\.$"} | %{$_.Name} | cp -Destination $targetDir -Exclude $omissions -recurse -ErrorAction silentlycontinue 
    ---------------------------------------------------------------------------------------
    

    The above does a directory listing of the source files in the base (selected "current") directory, filters out potential problem items, converts the file to the basename and forces cp (copy-item alias) to re-access the file "by name" in the "current directory" - thus reacquiring the file object, and copies it. This will create empty directories, including those that may even contain excluded files (less the exclusions of course). Note also that "ls" (get-childitem) does NOT -recurse - that is left to cp. Finally - if you're having problems and need to debug, remove the -ErrorAction silentlycontinue switch and argument, which hides a lot of nuisances that might interrupt the script otherwise.

    For those whose comments were related to "\" inclusions, keep in mind that you're working over the .NET sub-layer via an interpreter (i.e. PowerShell), and in c# for example, the inclusion of a single "\" (or multiple singles in a string), results in the compiler demanding you correct the condition by using either "\\" to escape the backslash, or precede the string with an @ as in @"\"; with the other remaining option being the enclosure of the string in single quotes, as '\'. All of this is because of ASCII interpolation of character combinations like "\n" etc.

    The latter is a much bigger subject, so I'll leave you with that consideration.

提交回复
热议问题