为什么Invoke-Command
远程运行命令与在完全相同的系统上本地运行完全相同的命令时产生的结果不同?
这些远程调用的命令显示 4 个空行:
PS > Invoke-Command cluster02 { cmd /c dir c:\ | select-string 'bytes free' }
虽然这些本地命令输出符合预期:
PS > Invoke-Command cluster02 { cmd /c dir c:\ | select-string 'bytes free' }
11 Dir(s) 828,179,570,688 bytes free
如果我先启动Enter-PSSession cluster02
然后运行它们,两个命令都会返回预期的输出。
这是旧版 PowerShell(Windows 2012)的一个错误,还是我错过了一些 PS 特性?如果这是一个错误或特性,它什么时候会表现出来?换句话说,从这种奇怪的行为中可以学到什么吗?
答案1
您遇到的问题是您正在尝试使用cmd /c
cmddir
命令远程运行,然后将其远程传输到 PowerShell 命令。
虽然您期望它在远程运行时能够像在本地一样工作,但众所周知,这会存在差异并造成混乱。
问题是,当您连接到远程计算机时,您并没有启动完整的用户环境。从技术上讲,您并不是按照通常意义上的“登录”到计算机。您是在进行身份验证,是的,但方式与您在共享文件夹中进行身份验证的方式大致相同。您的远程连接没有完整的用户配置文件,因此任何期望有完整用户配置文件的操作都可能出现错误并失败(即使它们没有显示这些错误)。
不幸的是,这个问题没有简单的解决办法。
当我针对远程机器运行时Invoke-Command -ScriptBlock {}
,我尝试将命令保留为 PowerShell。如果您混合使用 cmd 和 PowerShell,则应进行大量测试,因为它的工作方式可能与您在以交互方式登录系统时运行它的方式不同。
远程 PowerShell,获取驱动器可用空间(以字节为单位)(否cmd
)
Invoke-Command -ComputerName Cluster02 -ScriptBlock {(Get-PSDrive C).Free}
输出
599257099776
解决方法
将的输出设置Invoke-Command
为变量,然后从中选择字符串和行属性,这似乎可以达到您想要的输出效果。
PowerShell(变体 1)
$var = Invoke-Command cluster02 -ScriptBlock { cmd /c "dir c:\" };
($var| select-string 'bytes free').Line.Trim();
输出
11 Dir(s) 36,011,925,504 bytes free
问题很可能是由于cmd /c
在远程调用会话中调用然后 PowerShell 管道从中选择匹配的字符串。我通过将invoke-command
正确返回的完整输出保存为 之外的变量来解决该问题invoke-command
,然后在 之外相应地解析该变量invoke-command
。
PowerShell(变体 2)
这与上面的交易相同,但不是将变量混入其中,而是将权利传递Invoke-Command
到Select-String
查询。
Invoke-Command cluster02 -ScriptBlock { cmd /c "dir c:\"; } | select-string 'bytes free';
甚至像这样...
(Invoke-Command cluster02 -ScriptBlock { cmd /c "dir c:\"; } | select-string 'bytes free').Line.Trim()
答案2
(如果您喜欢这个 TLDR 版本,请对已接受的答案点赞)
TLDR 指南
远程运行脚本块时:
- 如果可能,请避免运行任何非原生 PowerShell。例如,
cmd /c dir c:\|sls 'bytes free'
用替换(Get-PSDrive C).Free
。 - 如果不是,则在远程 shell 中执行最少的处理。例如,将 select-string 移出远程 shell,如下所示:
Invoke-Command cluster02 { cmd /c "dir c:\" } | sls 'bytes free'
。