使用PowerShell捕获PsExec的输出会更改输出

使用PowerShell捕获PsExec的输出会更改输出,powershell,psexec,Powershell,Psexec,我试图实现的是重定向运行psexec-nobanner\\1.2.3.4 net localgroup Administrators时的标准输出和标准错误。重定向标准输出时,命令的结果会更改。以我尝试过的任何一种方式捕获标准输出似乎都会改变结果。我想知道为什么,我想让它工作 在PowerShell中,如果我运行以下命令: psexec -nobanner \\1.2.3.4 net localgroup Administrators 我看到: Couldn't access 1.2.3.4:

我试图实现的是重定向运行
psexec-nobanner\\1.2.3.4 net localgroup Administrators时的标准输出和标准错误。重定向标准输出时,命令的结果会更改。以我尝试过的任何一种方式捕获标准输出似乎都会改变结果。我想知道为什么,我想让它工作

在PowerShell中,如果我运行以下命令:

psexec -nobanner \\1.2.3.4 net localgroup Administrators
我看到:

Couldn't access 1.2.3.4:
The trust relationship between this workstation and the primary domain failed.
(当
无法访问1.2.3.4:
结束时,我短暂地看到
连接到1.2.3.4…
并且其他东西快速闪过而看不见。)

如果我尝试捕获输出,请使用以下命令:

$output = psexec.exe -nobanner \\1.2.3.4 net localgroup Administrators
我明白了:

Couldn't access 1.2.3.4:
The handle is invalid.
(如上所述,
无法访问1.2.3.4:
结束时,我简要地看到
连接到1.2.3.4…

我意识到我需要重定向错误流-这就是我开始的地方。但是我甚至不能在不改变它的情况下得到标准输出。这个问题是关于乳清的,一旦我试图捕捉它,输出就会发生变化

更新

我刚刚注意到,如果我运行相同的命令(在上面的PowerShell主机中工作)

在PowerShell ISE中,我得到如下相同的错误:

psexec : The handle is invalid.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (The handle is invalid.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

Connecting to 1.2.3.4...Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
那么,为什么在ISE中运行它会产生与普通主机不同的输出呢


我尝试过的其他事情:

1。启动流程

Start-Process -Wait -PSPath 'C:\Windows\PSTools\psexec.exe' -NoNewWindow `
    -ArgumentList "-nobanner \\$ip net localgroup Administrators" `
    -RedirectStandardError '.\tempError.log' -RedirectStandardOutput '.\tempOutput.log'
'O:'
Get-Content .\tempOutput.log
'E:'
Get-Content .\tempError.log
其中:

O:
E:
The handle is invalid.
Connecting to 1.2.3.4...


Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
The handle is invalid.
2。仅重定向标准输出

psexec -nobanner \\1.2.3.4 net localgroup Administrators > psexec.log
其中:

O:
E:
The handle is invalid.
Connecting to 1.2.3.4...


Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
The handle is invalid.
[psexec.log为空,因为我只重定向标准输出,而psexec将自己的消息写入标准错误。]

3。仅重定向标准错误

psexec -nobanner \\1.2.3.4 net localgroup Administrators > psexec.log
我还注意到了一些奇怪的地方:如果我只重定向标准错误,它可以工作(PsExec工作,命令失败,输出被重定向):

文件
psexec.log
包含:

psexec : The trust relationship between this workstation and the primary domain failed.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators 2> ps ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (The trust relat... domain failed.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

Connecting to 1.2.3.4...                                                                                        
                               Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...                                                                                        
4。全部重定向

psexec.exe -nobanner \\1.2.3.4 net localgroup Administrators *>&1 | Set-Variable -Name Output
因此:

psexec : The handle is invalid.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators *>&1  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (The handle is invalid.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

Connecting to 1.2.3.4...Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...

我使用
cmd
重复了上面的一些内容:

5。仅重定向标准输出,使用cmd

cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 1> psexec.log
cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 2> psexec.log
给出:

Couldn't access 1.2.3.4:
The handle is invalid.
(直接到控制台)。 6。仅重定向标准错误,使用cmd

cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 1> psexec.log
cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 2> psexec.log
给出(在
psexec.log
中):


psexec.exe是一个简单的可执行文件,它将输出写入stdout(标准输出)和stderr(标准错误)。因此,要捕获输出,请使用:

  • psexec.exe>stdout.txt
    捕获发送到stdout的数据
  • psexec.exe 2>sterr.txt
    捕获发送到stderr的输出
  • psexec.exe>combined.txt 2>&1
    在单个文件中捕获stdout和stderr

有趣的是,psexec将默认消息写入stderr——通常是stdout。因此,当运行psexec时,您在shell中看到的实际上是错误输出,需要使用
2>

psexec.exe是一个简单的可执行文件,它将输出写入stdout(标准输出)和stderr(标准错误)。因此,要捕获输出,请使用:

  • psexec.exe>stdout.txt
    捕获发送到stdout的数据
  • psexec.exe 2>sterr.txt
    捕获发送到stderr的输出
  • psexec.exe>combined.txt 2>&1
    在单个文件中捕获stdout和stderr

有趣的是,psexec将默认消息写入stderr——通常是stdout。因此,当运行psexec时,您在shell中看到的实际上是错误输出,需要使用
2>

捕获。这会将myScript.ps1中的所有输出返回到$result变量,而不包含psexec周围的所有其他垃圾。这是假设您可以将ps1文件复制到目标计算机

$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\myScript.ps1 2> $null
联机PS命令版本

$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command "get-process" 2> $null
命令行版本

$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\cmd.exe /c "ipconfig" 2> $null

这会将myScript.ps1的所有输出返回到$result变量,而不包含PsExec周围的所有其他垃圾。这是假设您可以将ps1文件复制到目标计算机

$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\myScript.ps1 2> $null
联机PS命令版本

$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command "get-process" 2> $null
命令行版本

$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\cmd.exe /c "ipconfig" 2> $null

这是使用psexec和运行空间从远程系统运行和收集报表数据的完整脚本。它比启动作业快得多,占用的内存也少得多

################################################################################################
# PSEXEC_Command_Runspaces
# Uses PSEXEC to run a command on multiple computers.  Useful when PS remoting is not enabled
# but you have admin rights.  
#
# Requires RSAT tools for the get-adcomputer command.  You could import a csv or other method
# to obtain a list of computers instead.  
################################################################################################
# Parameters
################################################################################################
#The list of computers to process
$pclist = get-adcomputer -filter "OperatingSystem -eq 'Windows 10 Enterprise' -and Name -like 'RC-*'" -properties DNSHostName | select -ExpandProperty DNSHostName
$Throttle = 500   #number of concurrent runspaces.  The higher this is, the more memory is needed for the runspaces.  500 takes less than 1GB for this script.  

################################################################################################
# This is the script that will run in each runspace.  
################################################################################################
$scriptblock = {
    Param (
      $nothing,  #this empty variable seems to be required because if you pass a single variable, it gets corrupted.  
      $PC
    )           

  if (test-connection $PC -Count 1 -ea SilentlyContinue) {  

    # Create script folders on remote computer and copy report script.  
    md \\$PC\c$\wsapps -ea SilentlyContinue
    md \\$PC\C$\wsapps\QA -ea SilentlyContinue
    copy 'C:\tools\Powershell\Review Center\PullBIOSandLoggedOnUser.ps1' "\\$pc\c$\wsapps\qa" -Force

    # Run ps exec and collect output
    # 2> $null gets rid of the "starting service and other junk from the PSexec output
    $result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\wsapps\qa\PullBIOSandLoggedOnUser.ps1 2> $null

    #remote script file from remote machine.  You could also remove folders if apropriate here. 
    remove-item \\$pc\C$\wsapps\QA\PullBIOSandLoggedOnUser.ps1 -ea SilentlyContinue

    #Parse results from single line of output.  PS does not return muliple lines of output from PSEXEC when wrapped in a job or runspace.  
    $parts = $result.split(",")
    $outobj = New-Object psobject
    $outobj | Add-Member ComputerName $PC
    $outobj | Add-Member WindowsVersion ($parts[1].split(":")[1])
    $outobj | Add-Member BiosVersion ($parts[2].split(":")[1])
    $outobj | Add-Member LoggedOnUser ($parts[3].split(":")[1])
    $outobj | Add-Member IPAddress ($parts[4].split(":")[1])
  }
  else {   #report object indicating offline status.
  $outobj = New-Object psobject
    $outobj | Add-Member ComputerName $PC
    $outobj | Add-Member WindowsVersion "Offline"
    $outobj | Add-Member BiosVersion "?"
    $outobj | Add-Member LoggedOnUser "?"
    $outobj | Add-Member IPAddress "?"
  }
  write-output $outobj
}


################################################################################################
# Main Logic
# Runspaces are much, much faster than start-job and use far less memory
# 260 computers took 4.5GB memory and > 20 minutes to process with start- job
# 260 computers took 260MB memory and < 1 minute to process with runspaces.
################################################################################################
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,$Throttle)
$RunspacePool.Open()

#RSArrayList contains a link to each runspace.  Needed to track progress and obtain results later
$RSArrayList = New-Object System.Collections.ArrayList   

#Loop through each PC in the list, creating runspaces.  The runspace pool is used for multiple parallel spaces with rate control.  
foreach ($PC in $PClist) {
  $PowerShell = [powershell]::Create()
  [void]$PowerShell.AddScript($scriptblock)
  [void]$powershell.AddArgument("").AddArgument($PC)  #extra argument to avoid single argument corruption bug.  
  $PowerShell.RunspacePool = $RunspacePool

  $ThisRS = New-Object psobject
  $ThisRS | Add-Member Computer $PC
  $ThisRS | Add-Member PSInstance $PowerShell
  $thisRS | Add-Member Space ($PowerShell.BeginInvoke())  #execution starts here.
  $RSArrayList += $thisRS
  write-host "Adding $PC"
}

################################################################################################
#Progress bar to track when jobs are finished.
write-host "waiting for runspaces to finish"
while (($RSArrayList.space.iscompleted -eq $false).count -gt 0) {
  $Done = $RSArrayList.count - ($RSArrayList.space.iscompleted -eq $false).count
  if ($Done -eq 0) {$percentComplete = 0}
  else {$percentComplete = $Done / $RSArrayList.count * 100}
  write-progress -Activity "Waiting for jobs to complete" -Status (($RSArrayList.count - $Done).ToString() + "Left") -PercentComplete $percentComplete
  sleep -Seconds 1
}

################################################################################################
#collecting results and creating report object
write-host "Processing Results"

$Report = New-Object System.Collections.ArrayList
foreach ($RS in $RSArrayList) {
  $Report += $RS.PSInstance.EndInvoke($RS.Space)  #equivilant to "receive-job"
  $RS.PSInstance.Dispose()  # frees up memory.
}

$Report | ft

这是使用psexec和运行空间从远程系统运行和收集报表数据的完整脚本。它比启动作业快得多,占用的内存也少得多

################################################################################################
# PSEXEC_Command_Runspaces
# Uses PSEXEC to run a command on multiple computers.  Useful when PS remoting is not enabled
# but you have admin rights.  
#
# Requires RSAT tools for the get-adcomputer command.  You could import a csv or other method
# to obtain a list of computers instead.  
################################################################################################
# Parameters
################################################################################################
#The list of computers to process
$pclist = get-adcomputer -filter "OperatingSystem -eq 'Windows 10 Enterprise' -and Name -like 'RC-*'" -properties DNSHostName | select -ExpandProperty DNSHostName
$Throttle = 500   #number of concurrent runspaces.  The higher this is, the more memory is needed for the runspaces.  500 takes less than 1GB for this script.  

################################################################################################
# This is the script that will run in each runspace.  
################################################################################################
$scriptblock = {
    Param (
      $nothing,  #this empty variable seems to be required because if you pass a single variable, it gets corrupted.  
      $PC
    )           

  if (test-connection $PC -Count 1 -ea SilentlyContinue) {  

    # Create script folders on remote computer and copy report script.  
    md \\$PC\c$\wsapps -ea SilentlyContinue
    md \\$PC\C$\wsapps\QA -ea SilentlyContinue
    copy 'C:\tools\Powershell\Review Center\PullBIOSandLoggedOnUser.ps1' "\\$pc\c$\wsapps\qa" -Force

    # Run ps exec and collect output
    # 2> $null gets rid of the "starting service and other junk from the PSexec output
    $result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\wsapps\qa\PullBIOSandLoggedOnUser.ps1 2> $null

    #remote script file from remote machine.  You could also remove folders if apropriate here. 
    remove-item \\$pc\C$\wsapps\QA\PullBIOSandLoggedOnUser.ps1 -ea SilentlyContinue

    #Parse results from single line of output.  PS does not return muliple lines of output from PSEXEC when wrapped in a job or runspace.  
    $parts = $result.split(",")
    $outobj = New-Object psobject
    $outobj | Add-Member ComputerName $PC
    $outobj | Add-Member WindowsVersion ($parts[1].split(":")[1])
    $outobj | Add-Member BiosVersion ($parts[2].split(":")[1])
    $outobj | Add-Member LoggedOnUser ($parts[3].split(":")[1])
    $outobj | Add-Member IPAddress ($parts[4].split(":")[1])
  }
  else {   #report object indicating offline status.
  $outobj = New-Object psobject
    $outobj | Add-Member ComputerName $PC
    $outobj | Add-Member WindowsVersion "Offline"
    $outobj | Add-Member BiosVersion "?"
    $outobj | Add-Member LoggedOnUser "?"
    $outobj | Add-Member IPAddress "?"
  }
  write-output $outobj
}


################################################################################################
# Main Logic
# Runspaces are much, much faster than start-job and use far less memory
# 260 computers took 4.5GB memory and > 20 minutes to process with start- job
# 260 computers took 260MB memory and < 1 minute to process with runspaces.
################################################################################################
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,$Throttle)
$RunspacePool.Open()

#RSArrayList contains a link to each runspace.  Needed to track progress and obtain results later
$RSArrayList = New-Object System.Collections.ArrayList   

#Loop through each PC in the list, creating runspaces.  The runspace pool is used for multiple parallel spaces with rate control.  
foreach ($PC in $PClist) {
  $PowerShell = [powershell]::Create()
  [void]$PowerShell.AddScript($scriptblock)
  [void]$powershell.AddArgument("").AddArgument($PC)  #extra argument to avoid single argument corruption bug.  
  $PowerShell.RunspacePool = $RunspacePool

  $ThisRS = New-Object psobject
  $ThisRS | Add-Member Computer $PC
  $ThisRS | Add-Member PSInstance $PowerShell
  $thisRS | Add-Member Space ($PowerShell.BeginInvoke())  #execution starts here.
  $RSArrayList += $thisRS
  write-host "Adding $PC"
}

################################################################################################
#Progress bar to track when jobs are finished.
write-host "waiting for runspaces to finish"
while (($RSArrayList.space.iscompleted -eq $false).count -gt 0) {
  $Done = $RSArrayList.count - ($RSArrayList.space.iscompleted -eq $false).count
  if ($Done -eq 0) {$percentComplete = 0}
  else {$percentComplete = $Done / $RSArrayList.count * 100}
  write-progress -Activity "Waiting for jobs to complete" -Status (($RSArrayList.count - $Done).ToString() + "Left") -PercentComplete $percentComplete
  sleep -Seconds 1
}

################################################################################################
#collecting results and creating report object
write-host "Processing Results"

$Report = New-Object System.Collections.ArrayList
foreach ($RS in $RSArrayList) {
  $Report += $RS.PSInstance.EndInvoke($RS.Space)  #equivilant to "receive-job"
  $RS.PSInstance.Dispose()  # frees up memory.
}

$Report | ft

如果尝试
psexec.exe-nobanner\\1.2.3.4 net localgroup Administrators*>&1 | Set Variable-Name输出
,会发生什么情况?psexec:句柄无效。第1行:1个字符:1+psexec-nobanner\\1.2.3.4网络本地组管理员*>&1…+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~CategoryInfo:NotSpecified:(句柄无效。:String)[],RemoteException+FullyQualifiedErrorId:NativeCommandError连接到1.2.3.4…无法访问1.2.3.4:连接到1.2.3.4…无法读取。请回答您的问题。如果尝试
psexec.exe-nobanner\\1.2.3.4 net localgroup Administrators*>&1 | Set Variable-Name输出
,会发生什么情况?psexec:句柄无效。第1行:1个字符:1+psexec-nobanner\\1.2.3.4网络本地组管理员*>&1…+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~CategoryInfo:NotSpecified:(句柄无效。:String)[],RemoteException+FullyQualifiedErrorId:NativeCommandError连接到1.2.3.4…无法访问1.2.3.4:连接到1.2.3.4…无法读取。请回答你的问题。