[英]Capturing output of PsExec using PowerShell changes the output
我要實現的目標是從運行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.
(在Couldn't access 1.2.3.4:
結束,我簡要地看到正在Connecting to 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.
(如上所述,在Couldn't access 1.2.3.4:
結束的地方,我簡要地看到了Connecting to 1.2.3.4...
)
我意識到我需要重定向錯誤流-這就是我開始的地方。 但是,即使不更改它,我什至無法獲得標准輸出。 這個問題是關於當我嘗試捕獲輸出時乳清輸出變化。
更新
我剛剛注意到,如果我運行相同的命令(上面在PowerShell主機中有效)
psexec -nobanner \\1.2.3.4 net localgroup Administrators
在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...
2.僅重定向標准輸出
psexec -nobanner \\1.2.3.4 net localgroup Administrators > psexec.log
這使:
Couldn't access 1.2.3.4:
The handle is invalid.
[psexec.log為空,因為我僅重定向標准輸出,而PsExec將其自身的消息寫入標准錯誤。]
3.僅重定向標准錯誤
我注意到了一些奇怪的事情:如果我僅重定向標准錯誤, 它就可以工作 (PsExec可以工作,命令失敗,輸出被重定向):
psexec -nobanner \\1.2.3.4 net localgroup Administrators 2> psexec.log
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
給出:
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 --% 2> 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是一個簡單的可執行文件,可將輸出寫入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
這是一個完整的腳本,用於使用psexec和Runspace從遠程系統運行和收集報告數據。 它比開始作業快得多,而且占用的內存少得多。
################################################################################################
# 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
這是在遠程系統上運行的報告收集腳本
################################################################################################
# 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.