如何在PowerShell复制脚本中正确过滤多个字符串

90

我正在使用这个回答中的PowerShell脚本进行文件复制。当我想要使用过滤器包括多个文件类型时,问题就出现了。

Get-ChildItem $originalPath -filter "*.htm"  | `
   foreach{ $targetFile = $htmPath + $_.FullName.SubString($originalPath.Length); ` 
 New-Item -ItemType File -Path $targetFile -Force;  `
 Copy-Item $_.FullName -destination $targetFile }

工作得像梦一样顺畅。然而,当我想使用筛选器包含多个文件类型时,问题就出现了。

Get-ChildItem $originalPath ` 
  -filter "*.gif","*.jpg","*.xls*","*.doc*","*.pdf*","*.wav*",".ppt*")  | `
   foreach{ $targetFile = $htmPath + $_.FullName.SubString($originalPath.Length); ` 
 New-Item -ItemType File -Path $targetFile -Force;  `
 Copy-Item $_.FullName -destination $targetFile }

给我以下错误:

Get-ChildItem : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Filter'. Specified method is not supported.
At F:\data\foo\CGM.ps1:121 char:36
+ Get-ChildItem $originalPath -filter <<<<  "*.gif","*.jpg","*.xls*","*.doc*","*.pdf*","*.wav*",".ppt*" | `
    + CategoryInfo          : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.GetChildItemCommand

我有不同的括号迭代、没有括号、-filter-include,定义包含变量(例如$fileFilter),每次都会得到上面的错误,并且总是指向跟在-filter后面的任何内容。

有趣的例外情况是当我编写 -filter "*.gif,*.jpg,*.xls*,*.doc*,*.pdf*,*.wav*,*.ppt*" 时。没有错误,但我没有结果,也没有返回到控制台。 我怀疑我无意中编码了一个隐式的and语句?

那么,我做错了什么,我该如何纠正它?

5个回答

218

-Filter 只接受一个字符串。 -Include 接受多个值,但要限定-Path参数。诀窍是在路径末尾添加\*,然后使用-Include选择多个扩展名。顺便说一下,在命令行工具的参数中引用字符串是不必要的,除非它们包含空格或shell特殊字符。

Get-ChildItem $originalPath\* -Include *.gif, *.jpg, *.xls*, *.doc*, *.pdf*, *.wav*, .ppt*

请注意,无论 $originalPath 是否以反斜杠结尾,这都有效,因为多个连续的反斜杠被解释为单个路径分隔符。例如,请尝试:

注意:无论 $originalPath 是否以反斜线结尾,此方法都有效,因为多个连续的反斜线将被解释为单个路径分隔符。例如,请尝试:

Get-ChildItem C:\\\\\Windows

7
哇塞!那个\*技巧刚刚解决了大约六个问题。太棒了,谢谢! - dwwilson66
21
注意,在指定了 -Recurse 参数时,无需使用通配符(\*)。 - Ohad Schneider
1
为什么在搜索目录时这个不起作用呢? - Ross Presser
1
@MarkLopez 我认为,如果你使用参数“recuse”而命令没有任何作用,那么它实际上是完美地工作了。抱歉,我忍不住 :) - MarredCheese
哈!谢谢你的回复,不过很遗憾,我无法再进行编辑了。 - Mark Lopez
显示剩余2条评论

6

让我们来看看选项:

  • -Filter only takes one pattern, so it doesn't work for this problem.

  • -Include works but is very slow (which is totally fine in many cases).

  • Piping to Where-Object is much faster than -Include. It is also the most powerful option because it gives you access to regex pattern matching (instead of the normal wildcard matching) and any other logic you might need, such as in the example below:

    # checking extension with regex
    Get-ChildItem $dir |
        Where-Object { $_.Extension -match '\.(xlsx?|jpe?g)$' }
    
    # checking extension and creation time
    Get-ChildItem $dir | Where-Object {
        $_.Extension -in '.xls', '.xlsx', '.jpg', '.jpeg' -and
        $_.CreationTime -gt $yesterday
    }
    
  • -Path is slightly faster still but takes full paths rather than filenames, which is a pain to work with (see examples below) and only works for simple cases because path patterns can't match a variable number of directory levels. This is in contrast to typical shells, in which * matches a single directory and ** matches any number of nested directories.

    # simpler
    $paths = $dir\*.xls, $dir\*.xlsx, $dir\*.jpg, $dir\*.jpeg
    Get-ChildItem $paths
    
    # less repetitive
    $paths = 'xls', 'xlsx', 'jpg', 'jpeg' | % { Join-Path $dir *.$_ }
    Get-ChildItem $paths
    

3

类似这样的代码应该可以工作(对我来说是有效的)。使用-Filter而不是-Include的原因是,与-Filter相比,-Include会导致巨大的性能损失。

以下代码循环遍历每个文件类型和在单独文件中指定的多个服务器/工作站。

##  
##  This script will pull from a list of workstations in a text file and search for the specified string


## Change the file path below to where your list of target workstations reside
## Change the file path below to where your list of filetypes reside

$filetypes = gc 'pathToListOffiletypes.txt'
$servers = gc 'pathToListOfWorkstations.txt'

##Set the scope of the variable so it has visibility
set-variable -Name searchString -Scope 0
$searchString = 'whatYouAreSearchingFor'

foreach ($server in $servers)
    {

    foreach ($filetype in $filetypes)
    {

    ## below creates the search path.  This could be further improved to exclude the windows directory
    $serverString = "\\"+$server+"\c$\Program Files"


    ## Display the server being queried
    write-host “Server:” $server "searching for " $filetype in $serverString

    Get-ChildItem -Path $serverString -Recurse -Filter $filetype |
    #-Include "*.xml","*.ps1","*.cnf","*.odf","*.conf","*.bat","*.cfg","*.ini","*.config","*.info","*.nfo","*.txt" |
    Select-String -pattern $searchstring | group path | select name | out-file f:\DataCentre\String_Results.txt

    $os = gwmi win32_operatingsystem -computer $server
    $sp = $os | % {$_.servicepackmajorversion}
    $a = $os | % {$_.caption}

    ##  Below will list again the server name as well as its OS and SP
    ##  Because the script may not be monitored, this helps confirm the machine has been successfully scanned
        write-host $server “has completed its " $filetype "scan:” “|” “OS:” $aSP:” “|” $sp


    }

}
#end script

这是非常正确的,在这里有5个类似的问题,没有人指出尽管我们不能做“-filter *.jpg, *.png”,但一次性使用-filter *.jpg可能仍然比使用-filter *.png并加入结果,或者使用一个“-Include *.jpg, *.png”更快。我有一个包含126k文件和18k文件夹的文件夹。我正在递归地搜索每个文件夹中的一个文件和一个文件夹。使用-Filter需要5秒钟,而使用-Include需要30秒钟。在10秒钟内两次使用-Filter比一次-Include更快三倍。 - papo

-1
Get-ChildItem $originalPath\* -Include @("*.gif", "*.jpg", "*.xls*", "*.doc*", "*.pdf*", "*.wav*", "*.ppt")

欢迎来到 Stack Overflow!这个答案出现在低质量审核队列中,可能是因为您没有解释内容。如果您能在回答中解释一下(这个问题的答案),您更有可能获得更多的赞,而且提问者也会真正学到东西! - The Guy with The Hat
2
此外,它重复了我之前发布答案中的代码,只是将扩展名列表包含在数组表达式求值运算符(@())中,这是多余的,因为逗号分隔的列表本身就被视为数组。 - Adi Inbar

-2

1
那没起作用。:( -filter -include *.file, *.types -filter -include (*.file, *.types), -filter -include "*.file", "*.types", 和 -filter -include ("*.file", "*.types") 都像我上面的问题一样出错了。消除 -filter 参数,只包括 -include(相同的引号和括号迭代)没有导致 运行时 错误,但目标目录中没有结果集。 - dwwilson66

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接