【问题标题】:Save an output-file from a cmdlet run in Invoke-Command on a remote machine to a local file将远程计算机上 Invoke-Command 中运行的 cmdlet 的输出文件保存到本地文件
【发布时间】:2020-06-26 01:24:12
【问题描述】:

我正在尝试在 LAN(而不是域)上的多台机器上运行以下命令。当我在本地机器上不使用 Invoke-Command 运行时,它运行良好。当我尝试调用时,它无法再在我正在运行命令的机器上找到文件路径,因为它正在查看远程目录。我无法为此目的使用共享驱动器。我有一个类似的问题,建议并成功实施了哈希表。我无法弄清楚我将如何使用以下内容来做到这一点。

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"
            }
}

【问题讨论】:

标签: powershell invoke-command


【解决方案1】:

解决此问题的一种方法是在数据返回后在本地处理数据,而不是在何时何地处理/收集数据。

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

无论如何,对于我的示例/演示,我将假设我们只需要 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

注意:我在域环境中,所以我关闭了-Credential

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

其他一些主要是糖的笔记:

  1. 我将脚本块放置在一个变量中。这只是我的一个偏好,我觉得使用起来更容易。
  2. 我没有使用 1 long if 条件,而是将属性存储在一个数组中。然后后来我循环测试每个属性的数组。如果任何属性命中我发出对象然后打破内部循环,所以我们不会得到重复。我不知道你为什么首先需要 if 条件,但这似乎是一种更有说服力的方法。

更新

响应您的 cmets,这是一个生成文件集的示例:

$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。我相信工作组中有一个关于远程处理的部分。

告诉我。谢谢。

【讨论】:

  • 谢谢。我现在就试试。我确实需要为空的冗余变量,因为其目的是创建一个 csv,该 csv 可以轻松地直接复制到具有这些命令未提供的附加参数的 excel 文件中。
  • 好的,我现在将添加该示例。
  • 设置完毕并尝试运行Invoke-Command foreach($computer in $computers) 在拉取本地计算机但没有从远程拉取任何信息时看起来很棒。我拔出 foreach 并在远程机器上运行并得到一个空白的 excel 作为回报。没有收到任何错误....
  • 我添加了第二个示例。似乎在我的环境中工作。我的示例并非设计为在 ForEach 循环中运行 Invoke-Command。所以我不确定你的意思。从技术意义上讲,这应该可行,但我无法确定您的环境中发生了什么。我有点担心它在工作组环境中的工作方式会有所不同。考虑一下,我将添加一些远程文档的链接。另外,不要急于求成,但如果您对此感到满意,请将问题标记为已回答。谢谢!
  • 我能够让远程计算机提取信息,但前提是我注释掉了 If($_.($Property))Break Inner。当然,我在 csv 上的每个属性都留下了一个条目……所以每个注册表项有 7 个条目。一旦我把它记下来,我肯定会立即将其标记为已回答。抱歉...我对此很陌生,非常感谢您的帮助。这看起来比我使用的要干净得多。
【解决方案2】:

雷切尔,

试试这个方法。

#--- 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

【讨论】:

  • 我真的不明白如何使用 Get-ItemProperty cmdlet 来提取数据。我需要能够使用Invoke-Command,但仍将我写出的所有变量保存到我正在运行脚本的机器上的excel文档中,并且不可能使用网络共享。我有我的$computers 位置集并且可以让脚本工作,但前提是我选择某些对象并导出到 csv。当我这样做时,我没有得到我需要的主机名和空空格
  • 我不明白你为什么不能在你运行的计算机上使用共享?我不知道有任何其他方法可以从远程计算机引用您计算机上的位置。另一种选择是构建一个大字符串。使用您的结果并将其作为 Invoke-Command 的结果传回,然后将其写入文件。通过不是很有效。
  • 这正是我必须为我的其他脚本做的事情。我希望能够使用共享,但我可能无权在我将要工作的系统上进行更改,并且我无法在我现在使用的虚拟 LAN 上对其进行测试(它只是无法工作)。我正在为这两种情况编写脚本,只是为了做好充分准备。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多