将两个不同的Get-ChildItem调用的结果合并到一个变量中,以便对它们进行相同的处理。

25
我正尝试编写一个PowerShell脚本来构建多个目录中的文件列表。当所有目录都添加到主列表后,我希望对所有文件执行相同的处理。
这是我的代码:
$items = New-Object Collections.Generic.List[IO.FileInfo]

$loc1 = @(Get-ChildItem -Path "\\server\C$\Program Files (x86)\Data1\" -Recurse)
$loc2 = @(Get-ChildItem -Path "\\server\C$\Web\DataStorage\" -Recurse)

$items.Add($loc1) # This line fails (the next also fails)
$items.Add($loc2)

# Processing code is here

出现以下错误:

无法将类型为“System.Object[]”的参数“0”,值为“System.Object[]”转换为类型“System.IO.FileInfo”: “无法将类型为“System.Object[]”的值转换为类型“System.IO.FileInfo”。”

我主要想知道这种情况下的正确方法是什么。我意识到我的代码很像 C 的写法--如果有更多PowerShell的解决方案可以完成同样的任务,我十分赞成。重点是,$loc# 的数量可能会随着时间的推移而发生变化,因此在生成的代码中添加或删除一两个应该很容易。

5个回答

38

不确定您是否需要在此处使用通用列表。 您可以只使用PowerShell数组,例如:

$items  = @(Get-ChildItem '\\server\C$\Program Files (x86)\Data1\' -r)
$items += @(Get-ChildItem '\\server\C$\Web\DataStorage\' -r)

使用 += 可以连接 PowerShell 数组。


4
也许需要解释一下 @() 的作用? - Peter Mortensen
如果输出只包含一个项目,则Get-ChildItem不会输出数组。 如果这不是PS,我们需要检查第一个输出的类型,Array vs Object,然后相应地处理它。 在PS中,(@(@('s','d')))[0].GetType()是String,这意味着忽略了创建仅包含一个数组的数组的意图。 在示例中,如果第一行只产生一个项目并且未使用@(),则$items将是Object,并尝试将数组连接到对象将在第二行引起错误。 第二行上的@()不是必需的,因为您可以通过相同的+=将对象连接到数组以及将数组连接到数组。 - papo
@papo | 如果输出仅包含一个项目,则Get-ChildItem不会输出数组。 尽管如此,它可以正常工作(至少在我的系统上,PS v5.1.14393.3471)。 我可以毫无问题地执行以下操作:$Files = @(gci -Filter "$BuildName-$SemanticVersion*.nupkg"); $Files += @(gci -Filter "Setup.exe"); $Files - InteXX
@InteXX 抱歉,我没有提到我的评论是对 Peter 评论的回答。你的例子类似于 Keith,所以应该是这样的,但是如果你省略 @(),就像 Peter 所问的那样,它可能会出现问题。PS 的自动输入很好,但可能会引起问题。如果你需要一个数组,请具体说明。否则,你可能会遇到错误,或在不同的情况下遇到一个意料之外的数组条目。我遵循这个规则,如果是脚本,要非常简洁。 - papo
@papo | 啊,好的。现在我明白了。那很有道理。实际上,我最终选择了下面Roman的版本。$Files = @(gci -Path $ReleaseDirectory -Filter "$BuildName-$SemanticVersion*.nupkg"; gci -Path $ReleaseDirectory -Filter "Setup.exe" gci -Path $ReleaseDirectory -Filter "RELEASES"); $Files 简洁明了,而且运行得很好。 - InteXX
显示剩余2条评论

30

从 get-help get-childitem: -Path 指定一个或多个位置的路径。 可以使用通配符。默认位置是当前目录(.)。

$items = get-childitem '\\server\C$\Program Files (x86)\Data1\','\\server\C$\Web\DataStorage\' -Recurse

8

这里有一种更加PowerShell风格的方式,不需要部分连接或明确地将项添加到结果中:

# Collect the results by two or more calls of Get-ChildItem
# and perhaps do some other job (but avoid unwanted output!)
$result = .{

    # Output items
    Get-ChildItem C:\TEMP\_100715_103408 -Recurse

    # Some other job
    $x = 1 + 1

    # Output some more items
    Get-ChildItem C:\TEMP\_100715_110341 -Recurse

    #...
}

# Process the result items
$result

但是脚本块内的代码应该更加小心地编写,以避免不想要的输出与文件系统项混合在一起。

编辑:或者,更有效的方法是,不使用 .{ ... },而是使用@( ... )$( ... ),其中...代表包含多个Get-ChildItem调用的代码。


5

Keith的回答是PowerShell的方式:只需使用@(...)+@(...)。

如果您确实想要一个类型安全的List[IO.FileInfo],那么您需要使用AddRange,并将对象数组强制转换为FileInfo数组--您还需要确保不会得到任何DirectoryInfo对象,否则您需要使用IO.FileSystemInfo作为列表类型:

因此,请避免目录:

$items = New-Object Collections.Generic.List[IO.FileInfo]
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Program Files (x86)\Data1\' -r | Where { -not $_.PSIsContainer } )) )
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Web\DataStorage\' -r | Where { -not $_.PSIsContainer } )) )

或者使用FileSystemInfo(FileInfo和DirectoryInfo的共同基类):
$items = New-Object Collections.Generic.List[IO.FileSystemInfo]
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Program Files (x86)\Data1\' -r)) )
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Web\DataStorage\' -r)) )

这是最好的、最简洁的方法。 - Tahir Hassan
我应该补充说明,在PS4+中,您现在可以使用Get-ChildItem(别名为ls)上的-file开关,而不是使用| where-object { -not $_.PSIsContainer } - Jaykul
我实际上是指你/Keith的解决方案 @(...) + @(...) :-) - Tahir Hassan
太棒了!我遇到了类似的问题,但是我正在使用JSON对象和ConvertFrom-JSON将其转换为PSCustomObject数组,使用了您的强制转换策略。好主意! - SliverNinja - MSFT

1

-Filter-Include 更高效,因此如果您没有很多不同的扩展名,简单地连接两个过滤列表可能会更快。

$files  = Get-ChildItem -Path "H:\stash\" -Filter *.rdlc -Recurse 
$files += Get-ChildItem -Path "H:\stash\" -Filter *.rdl  -Recurse 

我使用了类似于计时器的方法来比较输出:
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
# Do Stuff Here
$stopwatch.Stop()
Write-Host "$([Math]::Round($stopwatch.Elapsed.TotalSeconds)) seconds ellapsed"

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