【问题标题】:PowerShell Script that counts the files and folders in a number of servers then exports output to a CSV filePowerShell 脚本计算多个服务器中的文件和文件夹,然后将输出导出到 CSV 文件
【发布时间】:2019-06-07 09:39:10
【问题描述】:

我正在生成一个 PowerShell 脚本,它接收远程服务器列表并计算所述列表中每个服务器的每个驱动器上的所有文件和文件夹。似乎计算文件和导出到 csv 的代码可能已关闭。

以下是我开发的脚本的当前迭代:

#variable used to call the list of R servers
$ServerList = 'H:\R_SERVER_COUNT\AllServerNames.txt'

# Loops through all the servers on list
foreach ($server in Get-Content $ServerList) {
    $count = Get-ChildItem -r "$server\"

    $fcount = Get-WmiObject Win32-LogicalDisk -ComputerName '$server' |
              Select-Object @{Label="Server";Expression={$_.SystemName}},
                  @{Label="Drive";Expression={$_.DeviceID}},
                  @{Label="Files";Expression={($count | ? {$_.PSIsContainer -eq $false}).Count}},
                  @{Label="Folders";Expression={($count | ? {$_.PSIsContainer -eq $true}).Count}}
    $folderAndFileCount += $fcount
}

#creates a csv file and places it into folder
$folderAndFileCount |
    Export-Csv 'H:/R_SERVER_COUNT/FolderAndFileCount.csv' -NoTypeInformation -Encoding UTF8

我期望的是提供服务器名称、驱动器名称、文件数和文件夹数的 CSV 文件。 目前我收到

【问题讨论】:

  • Get-WmiObject Win32-LogicalDisk -ComputerName '$server' -> Get-WmiObject Win32_LogicalDisk -ComputerName $server
  • … -ComputerName "$server" 中使用双引号作为 ''$server'' 中的单引号导致计算机名称 $server(字面意思是 $server)。在任何情况下,直接查询Win32-LogicalDisk 可能需要非常长 时间...
  • 另外,Get-ChildItem -r "$server\" 可能不会像您认为的那样做。如果 $server 是服务器的主机名或 FQDN,请尝试使用 Get-ChildItem -r "\\$server\C$" 来枚举驱动器 C: 上的文件。更好的方法是使用Invoke-Command 在远程计算机上运行代码,该代码枚举计算机的驱动器、计算文件/文件夹并仅返回相应的计数。但是,请注意,无论哪种方式枚举计算机所有驱动器上的所有文件/文件夹都会非常缓慢。
  • 感谢 Ansgar,如何调用远程服务器上的所有驱动器,而不仅仅是驱动器 C:?
  • @RoonDog - 你知道Get-ChildItem 处理大量文件的速度有多慢 - 并且它遗漏了相当数量的文件吗? [grin] 对于我的 `C:` 驱动器,robocopy [仅设置为 /L 列表] 耗时 9 秒 并报告 d=49,914 & f=304,122 ///// gci 花了 12 分钟并报告了 d=41,243 & f=249,957 ///// windows 资源管理器花了 1 分钟并报告了 d=49,862 & f=304,129 ///// 你可能想要使用robocopy 并解析作业摘要。 [咧嘴]

标签: powershell csv


【解决方案1】:

虽然我认为 robocopy 可能是一个更好的解决方案 [它通常至少快一个数量级],但这将完成这项工作。请注意,我在测试时已将 -Recurse 注释掉。您将需要为真实的事物重新启用它。 [咧嘴一笑]

这使用Invoke-Command 在目标系统上并行运行。它会忽略不响应的系统,但如果您需要它们,您可以通过将输入列表与响应者进行比较来获取名称。

您可能还想使用Select-Object 删除Invoke-Command [PSComputerName、RunspaceID 等...] 添加的通常不需要的属性。

#requires -RunAsAdministrator

$ComputerNameList = @'
LocalHost
10.0.0.1
127.0.0.1
BetterNotBeThere
'@ -split [System.Environment]::NewLine

$IC_ScriptBlock = {
    $DriveList = Get-PSDrive -PSProvider 'FileSystem' |
        # exclude read-only devices such as ROM drives
        Where-Object {$_.Free -gt 0}

    foreach ($DL_Item in $DriveList)
        {
        $GCI_Params = @{
            LiteralPath = $DL_Item.Root
            # while testing, leave the "-Recurse" paramter out
            #Recurse =  $True
            ErrorAction = 'SilentlyContinue'
            Force = $True
            }
        $DF_Info = Get-ChildItem @GCI_Params
        $DirInfo = $DF_Info.Where({$_.PsIsContainer})
        [PSCustomObject]@{
            SystemName = $env:COMPUTERNAME
            Drive = $DL_Item.Root.Trim('\')
            DirCount = $DirInfo.Count
            FileCount = $DF_Info.Count - $DirInfo.Count
            }
        }
    }
$IC_Params = @{
    ComputerName = $ComputerNameList
    ScriptBlock = $IC_ScriptBlock
    ErrorAction = 'SilentlyContinue'
    }
$RespondingSystems = Invoke-Command @IC_Params

$RespondingSystems

一个系统的一个驱动器......

SystemName     : [MySysName]
Drive          : C:
DirCount       : 13
FileCount      : 3
PSComputerName : LocalHost
RunspaceId     : 0c67f714-ae8a-4831-b647-b27bd73ce94d

【讨论】: