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

Posted

技术标签:

【中文标题】如何在 PowerShell 复制脚本中正确过滤多个字符串【英文标题】:How to properly -filter multiple strings in a PowerShell copy script 【发布时间】:2013-09-08 02:56:11 【问题描述】:

我正在使用来自this answer 的 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

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

【问题讨论】:

【参考方案1】:

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

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

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

Get-ChildItem C:\\\\\Windows

【讨论】:

呜呼! \* 这个技巧刚刚解决了大约六个问题。太棒了,谢谢! 请注意,指定-Recurse时不需要通配符(\*)。 由于某种原因这在搜索目录时不起作用? 添加 -Recurse 允许它访问子文件夹 -Recuse 不适用于-Include(关于遍历子目录)。【参考方案2】:

这样的事情应该可以工作(它对我有用)。想要使用-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:” $a “SP:” “|” $sp


    


#end script

【讨论】:

这是非常正确的,在 5 个类似的问题中,没有人指出,虽然我们不能做 -filter *.jpg, *.png,但一次做 -filter *.jpg 可能比做更快-过滤 *.png 并加入结果,而不是一个 -Include *.jpg, *.png"。我有一个包含 126k 文件和 18k 文件夹的文件夹。我正在递归地在每个文件夹中搜索一个文件和一个文件夹。使用 -Filter 需要 5 秒,使用 -Include 需要 30 秒。做 - 在 10 秒内过滤两次比一次 - 包括 go ***​​倍。【参考方案3】:

让我们来看看选项:

-Filter 只采用一种模式,因此对于此问题不起作用

-Include 有效,但 very slow (在许多情况下完全没问题)。

连接到 Where-Object-Include 快得多。它也是最强大的选项,因为它使您可以访问 regex 模式匹配(而不是普通的通配符匹配)和您可能需要的任何其他逻辑,例如在下面的例子:

# 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 仍然稍微快一些,但使用完整路径而不是文件名,这很难使用(参见下面的示例)并且仅适用于简单的情况,因为路径模式无法匹配可变数量的目录级别。这与典型的 shell 不同,其中* 匹配单个目录,** 匹配任意数量的嵌套目录。

# 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

【讨论】:

【参考方案4】:
Get-ChildItem $originalPath\* -Include @("*.gif", "*.jpg", "*.xls*", "*.doc*", "*.pdf*", "*.wav*", "*.ppt")

【讨论】:

欢迎来到 Stack Overflow!这个答案出现在低质量审核队列中,大概是因为您没有解释内容。如果你确实解释了这一点(在你的回答中),你更有可能获得更多的支持——提问者实际上学到了一些东西! 此外,它重复了我之前发布的答案中的代码,除了它将扩展列表包含在数组表达式评估运算符 (@()) 中,这是多余的,因为逗号分隔list 本质上被评估为一个数组。【参考方案5】:

使用包含是最简单的方法

http://www.vistax64.com/powershell/168315-get-childitem-filter-files-multiple-extensions.html

【讨论】:

那行不通。 :( -filter -include *.file, *.types -filter -include (*.file, *.types)-filter -include "*.file", "*.types"-filter -include ("*.file", "*.types") 根据我上面的问题都出错了。消除 -filter 参数并只包括 -include (引号和括号的相同迭代)没有结果在 runtime 错误,但在目标目录中没有结果集。

以上是关于如何在 PowerShell 复制脚本中正确过滤多个字符串的主要内容,如果未能解决你的问题,请参考以下文章

powershell Quick Powershell脚本用于复制文件并使用文件类型过滤保留文件夹结构

powershell Quick Powershell脚本用于复制文件并使用文件类型过滤保留文件夹结构

Powershell where子句过滤

如何从我的 Powershell 脚本中的 Get-ADUser 过滤器获取结果,以便验证该用户是不是存在?

如何在 Azure DevOps 的 Powershell 内联脚本中正确连接字符串?

如何过滤 EventLog 以每天获取一个日志 - PowerShell