使用Powershell循环遍历数组

3

我想要在Active Directory中搜索所有Windows服务器的C和E驱动器,查找是否已存在putty.exe及其版本。输出需要包括服务器名称、可执行文件的完整路径和文件版本。到目前为止,我只有以下代码(现在仅用于测试两个服务器):

$ComputerName = Get-ADComputer -filter "name -like 'computer01' -or name `
-like 'server01'" | select -ExpandProperty name

$OutputArr = @()

$findFiles = foreach($computer in $computername){

    $file = Invoke-Command -computername $computer { Get-ChildItem -Path `
    c:\, e:\ -Recurse | where-object{(!$_.psiscontainer -eq $true) -and `
    ($_.name -like "putty.exe")} | ForEach-Object -process {$_.fullname} }


    $output = $OutputObj = New-Object -TypeName PSobject  
    $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $computer
    $OutputObj | Add-Member -MemberType NoteProperty -Name FilePath -Value $file

    $OutputArr += $OutputObj
    Write-Verbose $OutputObj
}

$OutputArr | fl

以上代码输出以下数组:
ComputerName : COMPUTER01
FilePath     : {C:\Program Files\PuTTY\putty.exe, C:\Program Files (x86)\PuTTY\PUTTY.EXE}

ComputerName : SERVER01
FilePath     : {C:\Program Files (x86)\putty\putty.exe, C:\Users\testuser\Desktop\Public Desktop\putty.exe}

这段代码产生了正确的数据,但现在我需要针对每个计算机名下的每个单独文件路径运行另一段代码片段,但我不确定如何实现,因为它正在使用包含多个条目的完整文件路径。

基本上,我需要将数组中的每个计算机名分成多行:

COMPUTER01,C:\Program Files\PuTTY\putty.exe
COMPUTER01,C:\Program Files (x86)\PuTTY\PUTTY.EXE
SERVER01,C:\Program Files (x86)\putty\putty.exe

数组不是正确的方法吗?

3个回答

3
如果您仅使用已存储在 $OutputArr 中的内容,则以下内容可行:
$out = foreach ($line in $OutputArr) {
   if ($line.filepath.count -gt 1) {
     foreach ($fp in $line.FilePath) {
       [pscustomobject][ordered]@{ComputerName = $line.ComputerName; FilePath = $fp}
     }
   } 
   else {
     $line
   }
 } 

$out | ConvertTo-Csv -NoTypeInformation

foreach循环将创建具有属性ComputerName和FilePath的新对象,并将它们作为对象数组存储在$out中。

如果您不关心属性,只想要逗号分隔的列表,可以使用以下内容:

foreach ($line in $OutputArr) {
  if ($line.filepath.count -gt 1) {
    foreach ($fp in $line.FilePath) {
      "{0},{1}" -f $line.ComputerName,$fp 
    }
  }
  else {
      "{0},{1}" -f $line.ComputerName,$line.FilePath
  }
 } 

这段代码与第一种解决方案的循环方式相同,但是使用格式运算符(-f)来格式化输出。将其管道传输到ConvertTo-Csv中可将输出格式化为以逗号分隔,并将属性作为标题。
在存储任何内容到$OutputArr之前,您可以将所需功能移动到代码中。我觉得在所有其他循环创建$OutputArr之后执行所有这些操作只会增加低效性。

0

我不确定您想要做什么,但这应该适用于迭代您的自定义对象。您的 Invoke-Command 也可以简化。

$file = Invoke-Command -computername $computer { Get-ChildItem -Path "C:\", "E:\" -Recurse -File -Filter "putty.exe" | Select -Property VersionInfo }

$OutputObj = New-Object -TypeName PSobject  
$OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $env:COMPUTERNAME
$OutputObj | Add-Member -MemberType NoteProperty -Name FilePath -Value $file

$OutputArr += $OutputObj

foreach ($item in $OutputArr)
{
    for ($i = 0; $i -lt $item.FilePath.Count; $i++)
    { 
         Write-Output ([string]::Join(', ', $item.ComputerName, $item.FilePath[$i].VersionInfo.FileName, $item.FilePath[$i].VersionInfo.FileVersion))
    }       
}

0

在进行远程会话时,PowerShell可能会变得棘手。下面的脚本应该是您的良好起点。以下是一些其他改进领域:

  1. 在驱动器的根目录执行Get-ChildItem -Recurse将使用过多的内存,您可能会导致意外的页面文件扩展,甚至使服务器因为100%的内存使用而无响应。在我的代码片段中,我正在使用一组众所周知的路径。如果您需要确定是否在其他计算机上启动了putty.exe,则您的监视解决方案希望具有进程性能数据,并且您可以在那里搜索putty.exe。
  2. 说到内存管理,远程shell有使用多少内存的限制。如果运行winrm get winrm/config/winrs,则会看到上限。
  3. 如果您要从远程脚本块中对其他资源进行身份验证,则需要设置支持双跳场景(CredSSP或Kerberos)的身份验证。
$computerNames = @('computer1','computer2')

foreach($computer in $computerNames)
{
    <# 
        First Script Block checks well known paths for putty.exe
    #>
    $puttyResults = Invoke-Command -ComputerName $computer -ScriptBlock {

        $wellKnownPaths = @()
        $wellKnownPaths += Join-Path $env:USERPROFILE -ChildPath "Desktop"
        $wellKnownPaths += "D:\tools\"
        $wellKnownPaths += $env:Path.Split(';')

        $puttyPaths = @()
        foreach($path in $wellKnownPaths)
        {
            $puttyPaths += Get-ChildItem $path -Filter "putty.exe" -Recurse
        }

        if($puttyPaths.Count -gt 0)
        {
            $resultsArray = @()
            foreach($path in $puttyPaths)
            {
                $resultsArray += [PSCustomObject]@{
                    ComputerName = $env:COMPUTERNAME
                    PuttyPath = $path.FullName
                }
            }

            return $resultsArray
        }

        return $null
    }

    if($puttyResults -ne $null)
    {
        foreach($result in $puttyResults)
        {
            <#
                Second script block takes action against putty.exe
            #>
            $puttyExists = Invoke-Command -ComputerName $computer -ArgumentList @($result.PuttyPath) -ScriptBlock {
                Param(
                    $PuttyPath
                )

                return (Test-Path $PuttyPath)
            }

            if($puttyExists)
            {
                $msg = "Putty exists on '{0}', at '{1}'" -f $result.ComputerName, $result.PuttyPath
                Write-Host $msg -ForegroundColor:Yellow
            }
        }        
    }
}

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