将远程计算机上 Invoke-Command 中的 cmdlet 运行 的输出文件保存到本地文件

Save an output-file from a cmdlet run in Invoke-Command on a remote machine to a local file

我正在尝试 运行 在 LAN(而非域)上的多台计算机上执行以下命令。当我 运行 在本地机器上没有 Invoke-Command 时,它工作得很好。当我尝试调用时,它无法再在我正在 运行 命令的机器上找到文件路径,因为它正在查看远程目录。为此,我无法使用共享驱动器。我有一个类似的问题,建议并成功实施了散列 table。我不知道如何用下面的方法做到这一点。

Invoke-Command -Computername $computers -Credential {
 Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation | Format-Table -Property DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation -AutoSize | Out-File -Width 2048 "c:\scripts\ComputerInformation\SoftwareInformation$env:COMPUTERNAME.software.txt"
        $applications = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
        foreach ($application in $applications) {
            $hostname = $env:COMPUTERNAME
            $DisplayName = $application.DisplayName
            $displayVersion = $application.DisplayVersion
            $HelpLink = $application.HelpLink
            $IdentifyingNumber = $application.PSChildName
            $InstallDate = $application.InstallDate
            $RegOwner = $null
            $vendor = $application.Publisher
            if ($DisplayName -ne $null -or $DisplayVersion -ne $null -or $HelpLink -ne $null -or $IdentifyingNumber -ne $null -or $InstallDate -ne $null -or $vendor -ne $null ) {
                Add-Content -Path 'c:\scripts\Inventories\SoftwareInventory.csv' "$hostname,$DisplayName,$DisplayVersion,$HelpLink,$IdentifyingNumber,$InstallDate,$RegOwner,$Vendor"
            }

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation | Format-Table -Property DisplayName,DisplayVersion,Publisher,InstallDate,InstallLocation -AutoSize | Out-File -Append -Width 2048 "c:\scripts\ComputerInformation\SoftwareInformation$env:COMPUTERNAME.software.txt"
        $applications = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
        foreach ($application in $applications) {
            $hostname = $env:COMPUTERNAME
            $DisplayName = $application.DisplayName
            $displayVersion = $application.DisplayVersion
            $HelpLink = $application.HelpLink
            $IdentifyingNumber = $application.PSChildName
            $InstallDate = $application.InstallDate
            $RegOwner = $null
            $vendor = $application.Publisher      
            if ($DisplayName -ne $null -or $DisplayVersion -ne $null -or $HelpLink -ne $null -or $IdentifyingNumber -ne $null -or $InstallDate -ne $null -or $vendor -ne $null ) {
                Add-Content -Path 'c:\scripts\Inventories\SoftwareInventory.csv' "$hostname,$DisplayName,$DisplayVersion,$HelpLink,$IdentifyingNumber,$InstallDate,$RegOwner,$Vendor"
            }
}

瑞秋,

这样试试。

#--- Set the path for location of NetComps.txt ---
$NetPath = "\MYBOOKLIVE\CMShared\Misc"
[Array]$Computer = Get-Content "$NetPath\NetComps.txt"

$OutPath = '\DellXPS8920\BEKDocs-DellXPS8920\Test.txt'

ForEach ($Comp in $Computer) {

  $TCArgs = @{ComputerName = "$Comp"
              Quiet        = $True
              Count        = 1}

  If (Test-Connection @TCArgs) {
    
    Try {
       $ICArgs = @{ComputerName = "$Comp"
                   ScriptBlock  = {gci -Path "G:\BEKDocs\Transfer\*.*"}
                   ErrorAction  = "Stop"
                  }
       $x = Invoke-Command @ICArgs  | Out-String

       If ($x -eq "") {
         Add-Content -Path "$OutPath" "Computer: $Comp - Directory Found but EMPTY"
       }
       Else {Add-Content -Path "$OutPath" "$x"}

    }
    Catch {Add-Content -Path "$OutPath" "Computer: $Comp - Directory Not Found"}

  } #End If (Test...
}   #End ForEach...

HTH

解决此问题的一种方法是在数据 returned 之后在本地处理数据,而不是在处理/收集数据的时间和地点。

有几件事让我感到困惑。您正在收集一些字符串数据并尝试将其放入文件中,然后尝试将有些冗余的其他数据放入第二个逗号分隔文件中。您还缺少凭证参数。

无论如何,对于我的示例/演示,我假设我们只需要 CSV 数据。

$ScriptBlock =
{
    $Properties = 
    @(
        'DisplayName'
        'DisplayVersion' 
        'HelpLink'
        'IdentifyingNumber'
        'InstallDate'
        'vendor'
    )
    
    Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
    ForEach-Object{
        :Inner ForEach( $Property in $Properties )
        {
            If( $_.$Property )
            {
                [PSCustomObject][Ordered]@{
                    hostname          = $env:COMPUTERNAME
                    DisplayName       = $_.DisplayName
                    DisplayVersion    = $_.DisplayVersion
                    HelpLink          = $_.HelpLink
                    IdentifyingNumber = $_.PSChildName
                    InstallDate       = $_.InstallDate
                    RegOwner          = $null
                    Vendor            = $_.Publisher
                }
            Break Inner
            }
        }
    }
}

Invoke-Command -Computername $computers -ScriptBlock $ScriptBlock |
Export-Csv -Append 'c:\scripts\Inventories\SoftwareInventory.csv' -NoTypeInformation

Note: I'm in a domain environment so I left -Credential off.

所以我正在 returning 适合直接输出到 CSV 的自定义对象。但关键是在脚本块中远程发出你想要的东西,让 PowerShell return 将它发送到本地会话,然后你可以在本地使用它。如果你有本地数据然后本地文件,没问题。

其他一些笔记主要是糖:

  1. 我把脚本块放在了一个变量中。这只是我的一个偏好,我发现它更容易使用。
  2. 我没有使用 1 long if 条件,而是将属性存储在数组中。然后我循环数组测试每个 属性。如果有任何 属性 命中,我会发出该对象,然后中断内部循环,这样我们就不会得到重复项。我不知道你为什么首先需要 if 条件,但这似乎是一种更 eloquent 的方式。

更新

为了回应您的评论,这里有一个生成文件集的示例:

$CsvProps =
@(
    'hostname'
    'DisplayName'
    'DisplayVersion' 
    'HelpLink'
    'IdentifyingNumber'
    'InstallDate'
    'vendor'
)

$TxtProps =
@(
    'DisplayName'
    'DisplayVersion'
    'Publisher'
    'InstallDate'
    'InstallLocation'
)

$ScriptBlock =
{
    Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
    ForEach-Object{
        [PSCustomObject][Ordered]@{
            hostname          = $env:COMPUTERNAME
            DisplayName       = $_.DisplayName
            DisplayVersion    = $_.DisplayVersion
            HelpLink          = $_.HelpLink
            IdentifyingNumber = $_.PSChildName
            InstallDate       = $_.InstallDate
            RegOwner          = $null
            Vendor            = $_.Publisher
            Publisher         = $_.Publisher
            InstallLocation   = $_.InstallLocation
        }
    }
}

$ReturnObjects = Invoke-Command -Computername $computers -ScriptBlock $ScriptBlock

# Create Textfiles for each host:
$ReturnObjects | 
Group-Object -Property hostname |
ForEach-Object{
    $FileName = $_.Name # This'll be the hostname, it was the group property.
    $_.Group |     
        Format-Table $TxtProps |
        Out-File -Width 2048 "c:\scripts\ComputerInformation\SoftwareInformation$FileName.software.txt"
    }

# Create CSV file:
$ReturnObjects |
ForEach-Object{
    :Inner ForEach( $Property in $_.PSObject.Properties.Name )
    {   # Write $_ to the pipeline if any property is valid
        If( $Property -eq 'hostname' ) { Continue Inner } # They will all have hostname.
        ElseIf( $_.$Property )
        {
            $_ # Write $_ down the pipeline
            Break Inner
        }
    }
    } |
Select-Object $CsvProps |
Export-Csv 'c:\scripts\Inventories\SoftwareInventory.csv' -NoTypeInformation -Append

所以这和以前的版本有点不同:

  1. 脚本块中的对象具有两个所需输出所需的所有属性。
  2. 这与我之前提到的方法不同。我认为最好将结果存储在一个变量中,而不是尝试在 1 ForEach-Object 循环中提取两个目标。
  3. 脚本块中没有if逻辑。这样我们就可以取回所有对象并在本地使用它们,这样我们就可以对文本和 csv 文件进行正确的过滤。同样,为了能够在本地写入文件,我们需要本地数据。
  4. 由于文本文件与 CSV 文件的属性不同,因此每个文件都有不同的集合。
  5. 我为CSV文件过滤数据的方式不同,但原理是一样的。

我意识到我的演示与您的示例有所不同。但是,我希望它能说明要点并帮助您解决问题。

仅供参考:如果您因为环境是工作组而遇到麻烦,check out Secrets of PowerShell Remoting。我相信在工作组中有一个关于远程处理的部分。

告诉我。谢谢。