【问题标题】:Recursively count files in subfolders递归计算子文件夹中的文件
【发布时间】:2014-08-26 13:06:05
【问题描述】:

我正在尝试计算目录中所有子文件夹中的文件并将它们显示在列表中。

例如以下脏:

TEST
    /VOL01
        file.txt
        file.pic
    /VOL02
        /VOL0201
            file.nu
            /VOL020101
                file.jpg
                file.erp
                file.gif
    /VOL03
        /VOL0301
            file.org

应该作为输出:

PS> DirX C:\TEST

Directory              Count
----------------------------
VOL01                      2
VOL02                      0
VOL02/VOL0201              1
VOL02/VOL0201/VOL020101    3
VOL03                      0
VOL03/VOL0301              1

我从以下开始:

Function DirX($directory)
{
    foreach ($file in Get-ChildItem $directory -Recurse)
    {
        Write-Host $file
    }
}

现在我有一个问题:为什么我的函数不递归?

【问题讨论】:

  • 您的代码 sn-p 确实按预期工作,它向我显示了 $directory 下的每个文件。目前的逻辑不会得到你想要的输出。

标签: powershell recursion


【解决方案1】:

这样的事情应该可以工作:

dir -recurse |  ?{ $_.PSIsContainer } | %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count }
  • dir -recurse 列出当前目录下的所有文件并将结果用管道 (|) 传递给
  • ?{ $_.PSIsContainer } 仅过滤目录,然后将结果列表再次通过管道传输到
  • %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count } 这是一个 foreach 循环,对于列表的每个成员 ($_) 显示全名和以下表达式的结果
  • (dir $_.FullName | Measure-Object).Count 提供 $_.FullName 路径下的文件列表,并通过 Measure-Object 计算成员数

  • ?{ ... } 是 Where-Object 的别名

  • %{ ... } 是 foreach 的别名

【讨论】:

  • 这行得通;好棒。但是你有没有可能在我开始的函数中多写一点?这很棒,但在这一点上对我来说就像黑魔法。我想先了解简单的脚本:-)
  • 感谢大卫的回答和精彩的解释。
  • @David 我正在尝试调整您的代码以输出到文件而不是控制台,但我无法弄清楚。请给我一些指导好吗?
  • 您可以通过管道传输到 Out-File ... | Out-File -FilePath
  • 如果要过滤特定文件(例如所有文件以“.txt”结尾的文件),可以修改此命令如下:dir -recurse | ?{ $_.PSIsContainer } | %{ Write-Host $_.FullName (dir $_.FullName | ?{$_ -match '.txt$'} | Measure-Object).Count }
【解决方案2】:

类似于 David 的解决方案,这将在 Powershell v3.0 中工作,并且不使用别名以防有人不熟悉它们

Get-ChildItem -Directory | ForEach-Object { Write-Host $_.FullName $(Get-ChildItem $_ | Measure-Object).Count}

答案补充

根据关于保持函数和循环结构的评论,我提供以下内容。 注意我不赞成这种解决方案,因为它很难看,而且内置的 cmdlet 可以很好地处理这个问题。不过我愿意提供帮助,所以这里是您的脚本的更新。

Function DirX($directory)
{
    $output = @{}

    foreach ($singleDirectory in (Get-ChildItem $directory -Recurse -Directory))
    {
        $count = 0 
        foreach($singleFile in Get-ChildItem $singleDirectory.FullName)
        {
            $count++
        }
        $output.Add($singleDirectory.FullName,$count)
    }

    $output | Out-String
}

对于每个 $singleDirectory 使用 $count 计数所有文件(在下一个子循环之前重置)并将每个发现输出到哈希表。最后将哈希表输出为字符串。在您的问题中,您看起来想要一个对象输出而不是纯文本。

【讨论】:

  • @Pr0no 我在答案中添加了一些可能更符合您的功能的内容。尽管提供的其他答案更多地证明了 powershell 的易用性和强大功能,但请理解。
  • 这个方法切断了长文件路径,但是它似乎比我的方法执行得更快。
【解决方案3】:

嗯,按照您的操作方式,整个Get-ChildItem cmdlet 需要在foreach 循环开始迭代之前完成。你确定你等得够久吗?如果你对非常大的目录(比如 C:) 运行它,这将需要很长时间。

编辑:看到您之前询问过一种使您的函数按照您的要求执行的方法,就这样吧。

Function DirX($directory)
{
    foreach ($file in Get-ChildItem $directory -Recurse -Directory )
    {
        [pscustomobject] @{
        'Directory' = $File.FullName
        'Count' = (GCI $File.FullName -Recurse).Count
        }
    }
}
DirX D:\

foreach 循环只获取目录,因为这是我们关心的全部内容,然后在循环内部为每次迭代创建一个自定义对象,其中包含文件夹的完整路径和文件夹内的项目数。

另外,请注意,这仅适用于 PowerShell 3.0 或更高版本,因为 -directory 参数在 2.0 中不存在

【讨论】:

  • 我实际上是在 OP 中给出的测试树上运行它。目前,该函数仅显示直接子代:VOL01VOL02VOL03
  • 我在 2.0 和 4.0 中都对其进行了测试,以确保它不是版本问题,并且在这两个版本中都为我提供了完整的结果。
  • 对不起,我的错误。我是 Powershell 新手(在 Powershell ISE 中工作),必须刷新我的脚本才能看到结果变化。我认为只保存脚本就足够了。现在我得到了子文件夹和文件的完整列表。如何列出每个文件夹的文件数?
  • 没问题,很高兴您能发现问题
  • @NoahSparks 考虑将$file 更改为其他内容。它代表一个目录而不是一个文件。并不是说它会影响脚本的功能。 +1,因为您比我更好地处理对象创建。
【解决方案4】:
Get-ChildItem $rootFolder `
    -Recurse -Directory | 
        Select-Object `
            FullName, `
            @{Name="FileCount";Expression={(Get-ChildItem $_ -File | 
        Measure-Object).Count }} 

【讨论】:

【解决方案5】:

我的版本 - 稍微干净一些并将内容转储到文件中

原创 - Recursively count files in subfolders

第二个组件 - Count items in a folder with PowerShell

$FOLDER_ROOT = "F:\"
$OUTPUT_LOCATION = "F:DLS\OUT.txt"
Function DirX($directory)
{
    Remove-Item $OUTPUT_LOCATION

    foreach ($singleDirectory in (Get-ChildItem $directory -Recurse -Directory))
    {
        $count = Get-ChildItem $singleDirectory.FullName -File | Measure-Object | %{$_.Count}
        $summary = $singleDirectory.FullName+"    "+$count+"    "+$singleDirectory.LastAccessTime
        Add-Content $OUTPUT_LOCATION $summary
    }
}
DirX($FOLDER_ROOT)

【讨论】:

    【解决方案6】:

    我稍微修改了 David Brabant 的解决方案,以便评估结果:

    $FileCounter=gci "$BaseDir" -recurse |  ?{ $_.PSIsContainer } | %{ (gci "$($_.FullName)" | Measure-Object).Count }
    Write-Host "File Count=$FileCounter"
    If($FileCounter -gt 0) {
      ... take some action...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-23
      • 1970-01-01
      • 2013-02-03
      • 2012-07-19
      相关资源
      最近更新 更多