要解决的问题:

我正在使用"调用命令"在远程计算机上执行脚本。

invoke-command -computername <server_name> -scriptblock {command to execute the script}

出现任何错误时,我的脚本将返回" -1"。 因此,我想通过检查返回代码来确保脚本已成功执行。

我尝试如下:

$result = invoke-command  -computername <server_name> -scriptblock { hostname }

但是它什么也没返回。

那么Invoke-command是否不捕获脚本块的返回码?

还有其他解决方法吗?

可以尝试的办法:

如果您在另一台服务器上以这种方式运行命令,则无法在该处获得脚本的返回代码。这是因为Invoke-Command可能仅在单个临时会话中在远程计算机上运行一个命令,而您无法再次连接到该会话。

但是,您可以做的是在远程计算机上创建一个会话并维护它,然后在该会话中调用脚本。之后,您可以再次检查该会话中的返回值。因此,遵循以下原则:

$mysession = New-PSSession -ComputerName <server_name>
Invoke-Command -Session $mysession -ScriptBlock { ... }
Invoke-Command -Session $mysession -ScriptBlock { $? }

例如:

$credential = Get-Credential
$mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
Invoke-Command -Session $mysession -ScriptBlock { hostname }
Invoke-Command -Session $mysession -ScriptBlock { $? }

工作组环境的winRM使用

被远程主机开启winRM(工作组)

我们可以通过多种方式开启被远程主机的winRM,但这不是本文要讲述的重点,有兴趣的可以翻看我之前的一些文章;但本文尝试会详细讲解通过直接在被远程主机上执行开启winRM指令的方式开启并通过winRM远程管理被管理主机。

Tips: 主机要开启winRM,方法是:

Enable-PSRemoting
Set-NetFirewallRule Name "WINRM-HTTP-In-TCP-PUBLIC" RemoteAddress Any
# 设置防火墙允许WINRM的访问

注意: "Run as administrator"

若您在开启过程中遇到下面的问题:

Powershell使用Invoke-Command捕获返回值_WSman

Powershell使用Invoke-Command捕获返回值_WSman_02

这种情况一般发生在计算机防火墙处于开启状态的情况

您可能知道Windows有三种网络连接类型:私有、公共和域。当您第一次连接到网络时,Windows将询问连接类型。您可以在网络和共享中心中设置不同的网络发现规则、文件和打印机共享规则。

上面的错误消息表明,为了启用PowerShell Remoting,我们已经将网络设置为Public。有几种方法可以更改连接类型。出于只有微软知道的某种原因,您不能在网络和共享中心中这样做。对于我启用PowerShell Remoting的目标,Metro接口中的其他选项都不起作用。

只需要添加-SkipNetworkProfileCheck参数:

Enable-PSRemoting -SkipNetworkProfileCheck -Force

Powershell使用Invoke-Command捕获返回值_PSSession_03

通过以下指令查看防火强rule开启情况:

PS C:\WINDOWS\system32> Get-NetFirewallRule -Name "WINRM*"

Name : WINRM-HTTP-In-TCP-NoScope
DisplayName : Windows (HTTP-In)
Description : WS-Management Windows [TCP 5985]
DisplayGroup : Windows
Group : @FirewallAPI.dll,-30267
Enabled : True
Profile : Domain, Private
Platform : {}
Direction : Inbound
Action : Allow
EdgeTraversalPolicy : Block
LooseSourceMapping : False
LocalOnlyMapping : False
Owner :
PrimaryStatus : OK
Status : (65536)
EnforcementStatus : NotApplicable
PolicyStoreSource : PersistentStore
PolicyStoreSourceType : Local
RemoteDynamicKeywordAddresses :

Name : WINRM-HTTP-In-TCP
DisplayName : Windows (HTTP-In)
Description : WS-Management Windows [TCP 5985]
DisplayGroup : Windows
Group : @FirewallAPI.dll,-30267
Enabled : True
Profile : Public
Platform : {}
Direction : Inbound
Action : Allow
EdgeTraversalPolicy : Block
LooseSourceMapping : False
LocalOnlyMapping : False
Owner :
PrimaryStatus : OK
Status : (65536)
EnforcementStatus : NotApplicable
PolicyStoreSource : PersistentStore
PolicyStoreSourceType : Local
RemoteDynamicKeywordAddresses :

Name : WINRM-HTTP-Compat-In-TCP-NoScope
DisplayName : Windows - (HTTP-In)
Description : WS-Management Windows [TCP 80]
DisplayGroup : Windows ()
Group : @FirewallAPI.dll,-30252
Enabled : False
Profile : Domain
Platform : {}
Direction : Inbound
Action : Allow
EdgeTraversalPolicy : Block
LooseSourceMapping : False
LocalOnlyMapping : False
Owner :
PrimaryStatus : OK
Status : (65536)
EnforcementStatus : NotApplicable
PolicyStoreSource : PersistentStore
PolicyStoreSourceType : Local
RemoteDynamicKeywordAddresses :

Name : WINRM-HTTP-Compat-In-TCP
DisplayName : Windows - (HTTP-In)
Description : WS-Management Windows [TCP 80]
DisplayGroup : Windows ()
Group : @FirewallAPI.dll,-30252
Enabled : False
Profile : Private, Public
Platform : {}
Direction : Inbound
Action : Allow
EdgeTraversalPolicy : Block
LooseSourceMapping : False
LocalOnlyMapping : False
Owner :
PrimaryStatus : OK
Status : (65536)
EnforcementStatus : NotApplicable
PolicyStoreSource : PersistentStore
PolicyStoreSourceType : Local
RemoteDynamicKeywordAddresses :



PS C:\WINDOWS\system32>

确认Action是Allow状态。

在远程主机上测试被远程主机的winRM开启状态

PS C:\Users\Administrator> Test-WSMan "xiamingliangpc"


wsmid : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor : Microsoft Corporation
ProductVersion : OS: 0.0.0 SP: 0.0 Stack: 3.0



PS C:\Users\Administrator>

以上显示代表对方已开启winRM功能。

如果远程的计算机没有加入域

在工作组计算机上,事情稍微复杂一些,在此过程中您可能会遇到一些额外的设置。

原因是启用PowerShell Remoting是一个安全风险,因为坏人肯定喜欢在您的计算机上远程自动化他们的黑客活动。因此,您必须设置一些额外的设定。

在Active Directory环境中,您可以只使用计算机名连接到远程计算机。如果远程连接到一台独立机器,则通常必须使用IP地址。如果您试图使用远程计算机的IP地址使用​ ​Enter-PSSession​ ​​或者​ ​New-PSSession​ ​ cmdlet连接到远程计算机,PowerShell将抛出以下错误:

注意:即使只要被远程主机开启的了​ ​winRM​ ​​并设置好防火墙允许策略,​ ​Test-WSMan​ ​​指令即可正常使用且不会报错;但实际能不能使用还得使用​ ​New-PSSession​ ​指令验证。

PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
New-PSSession : [DESKTOP001] xiamingliangpc : WinRM
Kerberos 使 HTTPS TrustedHosts
使 winrm.cmd TrustedHostsTrustedHosts
: winrm help config about_Remote_Troubleshooting
:1 : 14
+ ... mysession = New-PSSession -ComputerName "DESKTOP001" -Credential $cre ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotin
gTransportException
+ FullyQualifiedErrorId : ServerNotTrusted,PSSessionOpenFailed
PS C:\Users\Administrator>

这种情况下,你需要 修改远程主机的winrm设定 ;显示地允许管理被远程主机。(这里一定要注意是修改远程主机而不是被远程主机~~~)

我们首先检查下远程主机默认​ ​Enable-PSRemoting​ ​后;winrm的默认设定是什么。

PS C:\WINDOWS\system32> hostname
PCA001
PS C:\WINDOWS\system32> winrm get winrm/config/client
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = false
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = false
DefaultPorts
HTTP = 5985
HTTPS = 5986
TrustedHosts =

PS C:\WINDOWS\system32>

显示地设定TrustedHosts

然后我们显示地设定TrustedHosts;设置的方式有两种:

1.老式的命令

winrm set winrm/config/client @{TrustedHosts="192.168.11.199"}
# "192.168.11.149"是我用来远程管理xiamingliangpc机器的主机IP

2.powershell样式的命令

Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.11.149" -Force
# "192.168.11.149"是我用来远程管理xiamingliangpc机器的主机IP

以上两种方法任选其一即可;我个人偏向使用powershell样式的命令。

PS C:\WINDOWS\system32> Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.11.149" -Force
PS C:\WINDOWS\system32> winrm get winrm/config/client
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = false
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = false
DefaultPorts
HTTP = 5985
HTTPS = 5986
TrustedHosts = 192.168.11.199

PS C:\WINDOWS\system32> hostname
PCA001
PS C:\WINDOWS\system32>

# 或者也可以通过以下命令查看生效情况
PS C:\WINDOWS\system32> Get-Item WSMan:\localhost\Client\TrustedHosts


WSManConfig:Microsoft.WSMan.Management\WSMan::localhost\Client

Type Name SourceOfValue Value
---- ---- ------------- -----
System.String TrustedHosts 192.168.11.199


PS C:\WINDOWS\system32>

若您需要将winRM的TrustedHosts设置为*时,您可以使用下面的指令:

Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force

只能使用被远程主机的IP地址进行访问

完成以上设定,我们在远程主机上只能通过IP地址访问被远程主机:

PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "192.168.11.199" -Credential $credential
PS C:\Users\Administrator> Get-PSSession

Id Name ComputerName ComputerType State ConfigurationName Availability
-- ---- ------------ ------------ ----- ----------------- ------------
17 WinRM17 192.168.11.199 RemoteMachine Opened Microsoft.PowerShell Available


PS C:\Users\Administrator> Get-PSSession | Remove-PSSession
PS C:\Users\Administrator> Get-PSSession
PS C:\Users\Administrator>
PS C:\Users\Administrator>
PS C:\Users\Administrator>
PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
New-PSSession : [DESKTOP001] xiamingliangpc : WinRM
Kerberos 使 HTTPS TrustedHosts
使 winrm.cmd TrustedHostsTrustedHosts
: winrm help config about_Remote_Troubleshooting
:1 : 14
+ ... mysession = New-PSSession -ComputerName "DESKTOP001" -Credential $cre ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotin
gTransportException
+ FullyQualifiedErrorId : ServerNotTrusted,PSSessionOpenFailed
PS C:\Users\Administrator>

通过主机名或者IP均可访问的设定

提供一个逗号分隔的单个计算机名字符串

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'machineA,machineB'

或(危险)通配符

Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*'

追加到列表中,​ ​-Concatenate​ ​可以使用参数

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'machineC' -Concatenate

我们这里追加主机名到TrustedHosts

PS C:\WINDOWS\system32> Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'xiamingliangpc' -Concatenate

WinRM
WinRM TrustedHosts TrustedHosts
?
[Y] (Y) [N] (N) [S] (S) [?] (Y): y
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32>

查看设定结果:

PS C:\WINDOWS\system32> winrm get winrm/config/client
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = false
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = false
DefaultPorts
HTTP = 5985
HTTPS = 5986
TrustedHosts = 192.168.11.199,xiamingliangpc

PS C:\WINDOWS\system32> Get-Item WSMan:\localhost\Client\TrustedHosts


WSManConfig:Microsoft.WSMan.Management\WSMan::localhost\Client

Type Name SourceOfValue Value
---- ---- ------------- -----
System.String TrustedHosts 192.168.11.199,xiamingliangpc


PS C:\WINDOWS\system32>

尝试通过主机名远程访问被管理主机

PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
PS C:\Users\Administrator> Get-PSSession

Id Name ComputerName ComputerType State ConfigurationName Availability
-- ---- ------------ ------------ ----- ----------------- ------------
19 WinRM19 xiamingliangpc RemoteMachine Opened Microsoft.PowerShell Available


PS C:\Users\Administrator> Get-PSSession | Remove-PSSession
PS C:\Users\Administrator>

尝试远程执行指令并捕获返回值

# 1.建立session
PS C:\Users\Administrator> $mysession = New-PSSession -ComputerName "xiamingliangpc" -Credential $credential
# 2.在创建的session中远程执行指令
PS C:\Users\Administrator> Invoke-Command -Session $mysession -ScriptBlock { hostname }
xiamingliangpc
# 3.判定指定的执行成功与否
PS C:\Users\Administrator> Invoke-Command -Session $mysession -ScriptBlock { $? }
True
# 4.一个实际的例子:获取远程主机的最新10条system日志
PS C:\Users\Administrator> Invoke-Command -Session $mysession -ScriptBlock { Get-EventLog -LogName System -Newest 10 }

Index Time EntryType Source InstanceID Message PSComputerName
----- ---- --------- ------ ---------- ------- --------------
74990 9 16 14:11 Warning DCOM 10016 DCOM... xiamingliangpc
74989 9 16 14:09 Warning DCOM 10016 DCOM... xiamingliangpc
74988 9 16 13:43 Information Microsoft-Windows... 15 Microsoft-... xiamingliangpc
74987 9 16 13:43 Information Microsoft-Windows... 16 Microsoft-... xiamingliangpc
74986 9 16 13:43 Information Microsoft-Windows... 16 Microsoft-... xiamingliangpc
74985 9 16 13:43 Information Microsoft-Windows... 16 Microsoft-... xiamingliangpc
74984 9 16 13:43 Information Microsoft-Windows... 16 Microsoft-... xiamingliangpc
74983 9 16 13:43 Information Microsoft-Windows... 16 Microsoft-... xiamingliangpc
74982 9 16 13:43 Information Microsoft-Windows... 15 Microsoft-... xiamingliangpc
74981 9 16 13:42 Information Microsoft-Windows... 11 Microsoft-... xiamingliangpc

# 5.一个实际的例子:获取远程主机的最新10条system日志并将结果存放在本机变量中
PS C:\Users\Administrator> $result = Invoke-Command -Session $mysession -ScriptBlock { Get-EventLog -LogName System -Newest 10 }
PS C:\Users\Administrator> $result[0]

Index Time EntryType Source InstanceID Message PSComputerName
----- ---- --------- ------ ---------- ------- --------------
74990 9 16 14:11 Warning DCOM 10016 DCOM... xiamingliangpc


PS C:\Users\Administrator>

# 6.用完的session注意要关闭
PS C:\Users\Administrator> $mysession | Remove-PSSession
PS C:\Users\Administrator>

本文到这里就结束了,做个总结。

总结

想要在远程执行powershell的情况下获得远程指令的返回值,我们需要:

1.管理主机和被管理主机都要开启(工作组环境)winRM

2.在管理主机上添加被管理主机的IP或者计算机名到winRM的TrustedHosts中

3.建立PSSession

4.在建立的PSSession中Invoke-Command

5.记得在PSSession使用完成后关闭PSSession

希望大家本文对大家有所帮助。

Powershell 并发任务 | Runspace 线程 | 结果获取

在 PowerShell 中进行多任务处理(Multithreading 或 Parallel Processing)主要目的是提高脚本的执行效率和性能。对于需要处理大量数据或执行多个独立任务的脚本来说尤其有用。1. **提高性能:** 多任务处理允许脚本同时执行多个任务,从而加快整体执行速度。对于需要处理大型数据集或执行耗时的操作时尤为重要。2. **充分利用多核处理器:** 现代计算机配备了多核处理器。通过多任务处理,充分发挥这些多核处理器的性能,提高整体计算能力。3. **并行执行独立任务:** 在某些情况下,脚本需要执行多个相互独立的任务。多任务处理使得这些任务同时进行,而不是依次执行,提高脚本的效率。