简体   繁体   English

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

[英]Capturing output of PsExec using PowerShell changes the output

What I'm trying to achieve is to redirect standard output and standard error from running psexec -nobanner \\\\1.2.3.4 net localgroup Administrators . 我要实现的目标是从运行psexec -nobanner \\\\1.2.3.4 net localgroup Administrators重定向标准输出和标准错误。 When I redirect standard output, the result of the command changes. 当我重定向标准输出时,命令的结果将更改。 Something about capturing standard output, in any of the ways I've tried, seems to change the result. 以我尝试过的任何方式,有关捕获标准输出的某些事情似乎都会改变结果。 I'd like to know why and I'd like to get it working. 我想知道为什么,并且想让它正常工作。

In PowerShell, if I run this: 在PowerShell中,如果运行此命令:

psexec -nobanner \\1.2.3.4 net localgroup Administrators

I see this: 我看到这个:

Couldn't access 1.2.3.4:
The trust relationship between this workstation and the primary domain failed.

(Where Couldn't access 1.2.3.4: ends up, I briefly see Connecting to 1.2.3.4... and something else flashes past too quickly to see.) (在Couldn't access 1.2.3.4:结束,我简要地看到正在Connecting to 1.2.3.4...而另一些东西闪烁得太快而无法看到。)

If I try to capture the output, using this: 如果我尝试捕获输出,请使用以下命令:

$output = psexec.exe -nobanner \\1.2.3.4 net localgroup Administrators

I see: 我懂了:

Couldn't access 1.2.3.4:
The handle is invalid.

(As above, where Couldn't access 1.2.3.4: ends up, I briefly see Connecting to 1.2.3.4... .) (如上所述,在Couldn't access 1.2.3.4:结束的地方,我简要地看到了Connecting to 1.2.3.4...

I realise that I need to redirect the error stream - that's where I started. 我意识到我需要重定向错误流-这就是我开始的地方。 But I can't even get the standard output without it changing. 但是,即使不更改它,我什至无法获得标准输出。 This question is about whey the output changes as soon as I try to capture it. 这个问题是关于当我尝试捕获输出时乳清输出变化。

UPDATE 更新

I've just noticed that if I run the same command (that works above in the PowerShell host) 我刚刚注意到,如果我运行相同的命令(上面在PowerShell主机中有效)

psexec -nobanner \\1.2.3.4 net localgroup Administrators

in PowerShell ISE , I get the same error as below: 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...

So, why does running it in ISE give different output from the normal host? 那么,为什么在ISE中运行它会产生与普通主机不同的输出?


Other things I've tried: 我尝试过的其他方法:

1. Start-Process 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

which gives: 这使:

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

2. Redirect Standard Output only 2.仅重定向标准输出

psexec -nobanner \\1.2.3.4 net localgroup Administrators > psexec.log

which gives: 这使:

Couldn't access 1.2.3.4:
The handle is invalid.

[psexec.log is empty because I'm only redirecting Standard Output and PsExec writes its own messages to Standard Error.] [psexec.log为空,因为我仅重定向标准输出,而PsExec将其自身的消息写入标准错误。]

3. Redirect Standard Error only 3.仅重定向标准错误

I've noticed something else odd about this: if I only redirect Standard Error, it works (PsExec works, the command fails, the output is redirected): 我注意到了一些奇怪的事情:如果我仅重定向标准错误, 它就可以工作 (PsExec可以工作,命令失败,输出被重定向):

psexec -nobanner \\1.2.3.4 net localgroup Administrators 2> psexec.log

The file psexec.log contains: 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. Redirect all 4.全部重定向

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

which gives this: 这给出了:

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

I repeated some of the above using cmd : 我使用cmd重复了上述内容:

5. Redirect Standard Output only, with cmd 5.仅使用cmd重定向标准输出

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

gives: 给出:

Couldn't access 1.2.3.4:
The handle is invalid.

(directly to the console). (直接到控制台)。 6. Redirecting Standard Error only, with cmd 6.仅使用cmd重定向标准错误

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

gives (in psexec.log ): 给出(在psexec.log ):

The trust relationship between this workstation and the primary domain failed.
Connecting to 1.2.3.4...


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

psexec.exe is a simple executable which writes output to stdout (standard output) and stderr (standard error). psexec.exe是一个简单的可执行文件,可将输出写入stdout(标准输出)和stderr(标准错误)。 So, to capture the output use: 因此,要捕获输出,请使用:

  • psexec.exe > stdout.txt to capture sent to stdout. psexec.exe > stdout.txt将捕获发送到stdout。
  • psexec.exe 2> sterr.txt to capture output sent to stderr. psexec.exe 2> sterr.txt以捕获发送到stderr的输出。
  • psexec.exe > combined.txt 2>&1 to capture both stdout and stderr in a single file. psexec.exe > combined.txt 2>&1可以将stdout和stderr捕获到一个文件中。

Interestingly, psexec writes the default message to stderr - usually this would be stdout. 有趣的是,psexec将默认消息写入stderr-通常为stdout。 So what you see in your shell when running psexec is actually the error output and would need to be captured using 2> . 因此,运行psexec时在shell中看到的实际上是错误输出,需要使用2>捕获。

This returns all the output from myScript.ps1 to the $result variable without all the other junk surrounding PsExec. 这会将myScript.ps1的所有输出返回到$ result变量,而PsExec周围没有其他所有垃圾。 This is assuming you can get the ps1 file copied to the target machine. 假设您可以将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

In line PS command version 符合PS命令版本

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

Command line version 命令行版本

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

This is a full script to run and collect report data from remote systems using psexec and runspaces. 这是一个完整的脚本,用于使用psexec和Runspace从远程系统运行和收集报告数据。 It is much, much faster than start-job and uses far less memory. 它比开始作业快得多,而且占用的内存少得多。

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

This is the report collection script that is run on the remote system 这是在远程系统上运行的报告收集脚本

################################################################################################
# Looks up the computer name, Windows Version, BIOS version, logged on user, and IP address of the computer.  
# Designed to be called by start-job or runspaces (much faster).  
################################################################################################
$computername = $env:COMPUTERNAME
$WindowsVersion = (Get-WmiObject win32_OperatingSystem).BuildNumber.toString()

$BiosVersion = (Get-WmiObject Win32_BIOS).Name
$IPAddress = "No 10 range IP"
$addr = (Get-NetIPAddress -AddressFamily IPv4).ipaddress | where {$_ -like '10.*'}
if ($addr) {$IPAddress = $addr}
$LoggedOnUser = "None"
$quser = (quser.exe 2> $null | select-string "console").line
if ($quser) {$LoggedOnUser = $quser.Substring(1,$quser.IndexOf(" ",1)-1)}

# For whatever reason, PS will not return multiple lines of output from PSexec when run under start-job or runspaces.  This was the workaround.  
"Computername:$computername,WindowsVersion:$WindowsVersion,BIOSVersion:$BIOSVersion,LoggedOnUser:$LoggedOnUser,IPAddress:$IPAddress"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM