首先,这是我在stackoverflow上的第一篇文章。如果我做错了什么,请告诉我。
好吧,我是一个"初级"系统管理员,几个月前我开始学习Powershell。
我正在编写一个脚本来断开特定帐户与域中所有服务器的连接,为此我执行希望通过多个PSSessions执行此命令块:
if ((Get-Process -IncludeUserName).UserName -match 'myusername') {
$SessionIds = ((Get-Process -IncludeUserName | Where username -Match "myusername") | select SessionId -Unique)
foreach($SessionId in $SessionIds) {
logoff $SessionId
}
}
如果我的逻辑很好,如果 myusername 下至少有一个进程处于活动状态,则 if 语句将为真。但我的问题是,当我运行这个特定的行时(Get-Process -IncludeUserName).UserName -match 'myusername')
它不会返回 true 或 false,它只是列出我的用户名与其下有一个进程一样多的时间。
但是,如果我指定一个进程名称(必须只有一个进程在该名称下运行),例如示例"资源管理器",因此:(Get-Process explorer -IncludeUserName).UserName -match 'myusername')
我有"真实"的回应。
我知道我可以这样做...但这让我很困扰。不仅如此!即使我的会话中没有运行资源管理器进程,也可以记录我的用户。
无论如何,我希望我说清楚了。英语不是我的母语(就像我们这里的许多人一样)。
提前感谢您的参与!祝你今天开心。
解释-Match
行为
首先,字符串上的-Match
通过匹配模式(.NET 常规执行)并返回$true
或$false
来工作。仅指定一个没有其他表达式标记的字符串将匹配任何出现的myusername
,因此在myusername2
和supermyusername
上也会匹配。在这种情况下,最好使用-eq
,或者如果您想匹配已知格式的用户名,请使用-Match
指定正确的表达式。
但是,在集合上使用-Match
时,行为略有不同。您注意到单个UserName
上的-Match
返回$true
但是当有多个实例时,它会返回该UserName
的许多实例。这是因为对集合使用-Match
实际上会返回与集合中该模式匹配的对象。如果您要将-Match 'myusername'
更改为-Match 'username
,您会发现您仍然会返回很多myusername
。
解决主要问题
若要解决"用户可能有多个进程"问题,请通过管道将UserName
数组传送到Select-Object -First 1
以选择数组的第一个元素。您需要选择Get-Process
返回的第一项,因为Get-Process
将返回所有进程,并且您的Where-Object
子句可能在具有相同UserName
的多个进程上匹配:
if ((Get-Process -IncludeUserName | Select-Object -First 1).UserName -match 'myusername') {
....
}
现在,您不再依赖于希望一个进程(explorer.exe
)只有一个实例才能使脚本工作。现在我们了解了它是如何工作的,这里有一种更好的方法可以实现您想要的。
让它变得更好(Hey Jude)
我们可以利用一种称为变量挤压的更高级技术来获取匹配进程的列表,并确保它实际上一次性返回了一些东西:
if ( ( $proc = Get-Process -IncludeUserName | Where-Object UserName -eq 'myusername' ) ){
$sessionIds = $proc.SessionId | Select-Object -Unique
foreach( $id in $sessionIds ) {
logoff $id
}
}
让我解释一下这段代码做了什么,而你的原始版本没有做到:
($proc = Get-Process -IncludeUserName | Where-Object UserName -eq 'myusername')
- 在
()
之间放置变量赋值称为变量挤压,这是一种为变量赋值同时将其输出到管道的方法。请注意,$proc
在if
子句中分配后仍然可用。 - 如果没有返回任何进程并将其分配给
$proc
,则if
子句的评估结果将$false
。这篇博文很好地解释了PowerShell的真实性。
- 在
$sessionIds = $proc.SessionId | Select-Object -Unique
Select-Object -Unique
将返回您传入的任何内容的单序化集合。换句话说,在这种情况下,这将删除重复的SessionIds
。- 你最初的策略是让
explorer.exe
认为只有一个实例是有缺陷的。如果将资源管理器配置为在与主进程不同的进程中启动,则explorer.exe
绝对可以在多个实例上匹配。即使你认为没有人会设置它,只需要一个高级用户来破坏你的脚本。