我有一个功能可以与用户比较进程,但是当我尝试将输出通过管道传递到停止进程时,出现以下错误:
“Stop-Process:无法评估参数'InputObject',因为其参数被指定为脚本块
而且没有输入。没有输入就无法评估脚本块。 ”
Function Proc {
$TargetUsers = get-content oldusers.txt
$WmiArguments = @{
'Class' = 'Win32_process'
}
$processes = Get-WMIobject @WmiArguments | ForEach-Object {
$Owner = $_.getowner();
$Process = New-Object PSObject
$Process | Add-Member Noteproperty 'ComputerName' $Computer
$Process | Add-Member Noteproperty 'ProcessName' $_.ProcessName
$Process | Add-Member Noteproperty 'ProcessID' $_.ProcessID
$Process | Add-Member Noteproperty 'Domain' $Owner.Domain
$Process | Add-Member Noteproperty 'User' $Owner.User
$Process
}
ForEach ($Process in $Processes) {
if ($TargetUsers -Contains $Process.User) {
stop-process -id {$_.processid}
}
}
}
我已经阅读了有关Powershell中管道命令的服务器文章,并且它是基于对象的,但是我迷失了如何使用一个函数的返回对象并将其管道化为另一个函数,即使我确信当您知道如何简单时也是如此
最佳答案
我喜欢西奥的答案。我只想添加一些肯定不会包含在评论中的其他内容...
最初,我认为我们都在调试您的代码,但要保持最初设置的模式。严格来说,这没有错。
我认为您真正的问题是这里有些失落。为什么不能简单地将一个函数的输出传递给另一个函数呢?答案在于接收cmdlet或函数如何期待数据。
如果查看Get-Help Stop-Process -Parameter Id
(OR参数名称),您将看到,如果正确命名属性,它将通过管道获取该属性:
-Id <Int32[]>
Specifies the process IDs of the processes to stop. To specify multiple IDs, use commas to separate the IDs. To find the PID of a process, type `Get-Process`.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
因此,如果您是自定义对象,则该管 Prop 有名为“Id”的属性。Stop-Process
将接受进程ID,但它正在寻找的属性名称为“Id”,而Win32_Process返回“ProcessID”。但是,还有第二个问题。传入的属性值必须是接收函数/ cmdlet可接受的。不幸的是,Win32_Process通常返回带有“.exe”后缀的名称,而
Get-Process
将不接受该名称。Theo的答案非常好,可以与
Stop-Process
一起使用,因为他的新对象具有一个名为“ID”的属性,该属性被管道接受并且是默认参数集的一部分,这意味着它比name属性更受青睐。但是,如果要将这些对象通过管道传递给
Get-Process
,将无法正常工作。 Get-Process
优先使用名称而不是ID,并且期望使用类似“notepad”而不是“Notepad.exe”的值,Win32_Process返回该值。在这种情况下,Get-Process
将无法找到该进程,并且会出错。注意:以上已根据与Theo的协作进行了更正,您可以查看以前的修订和注释以供引用。
为了使对象也可以与
Get-Process
一起使用,只需修改进入“Name”属性的值以删除结尾的'.exe'即可。我编辑了Theo的答案只是为了补充一点。您应该看到他是否批准了。我意识到这不是您最初的问题的一部分,而是要说明不同工具/ cmdlet /功能等之间管道的附加警告。
注意:可能仍然有几个异常(exception)。例如:Win32_Process返回“系统空闲进程”,但是
Get-Process
返回“空闲”。为了您的目的,这可能不是问题。当然,您永远不会停止该过程!注意:
Get-Process
首选名称而Stop-Process
首选ID的可能原因是名称不是唯一的,而ID是。 Stop-Process
记事本将杀死记事本的所有实例,通常(在您的情况下)不是预期的。关于一般方法。我指出了扩展对象和创建PS Custom对象的几种方法。如果您需要或希望实例类型保持不变,
Add-Member
是一个很好的方法。我会考虑扩展对象。但是,在您的情况下,您要创建一个自定义对象,然后向其中添加成员。在这种情况下,我通常使用已经转换为PSCustomObjects的Select-Object。您的代码具有更正的“名称”属性:
$ Processes = Get-WmiObject win32_process
$Processes |
ForEach-Object{
$Owner = $_.getowner()
$Process = New-Object PSObject
$Process | Add-Member NoteProperty 'ComputerName' $_.CSName
$Process | Add-Member NoteProperty 'ProcessName' $_.ProcessName
$Process | Add-Member NoteProperty 'ProcessID' $_.ProcessID
$Process | Add-Member NoteProperty 'Domain' $Owner.Domain
$Process | Add-Member NoteProperty 'User' $Owner.User
$Process | Add-Member NoteProperty 'Name' -Value ( $_.ProcessName -Replace '\.exe$' )
$Process
}
注意:为简洁起见,我删除了一些周围的代码。使用select看起来像:
$Processes = Get-WmiObject win32_process |
Select-Object ProcessID,
@{Name = 'ComputerName'; Expression = { $_.CSName }},
@{Name = 'Name '; Expression = { $_.ProcessName -Replace '\.exe$' } },
@{Name = 'Id'; Expression = { $_.ProcessID } },
@{Name = 'Domain'; Expression = { $_.GetOwner().Domain} },
@{Name = 'User'; Expression = { $_.GetOwner().User} }
然后,可以将其直接传递给where子句以过滤正在寻找的进程,然后再次传递给Stop-Process
cmdlet:Get-WmiObject win32_process |
Select-Object ProcessID,
@{Name = 'ComputerName'; Expression = { $_.CSName }},
@{Name = 'Name '; Expression = { $_.ProcessName -Replace '\.exe$' } },
@{Name = 'Id'; Expression = { $_.ProcessID } },
@{Name = 'Domain'; Expression = { $_.GetOwner().Domain} },
@{Name = 'User'; Expression = { $_.GetOwner().User} } |
Where-Object{ $TargetUsers -contains $_.User } |
Stop-Process
注意:这甚至将分配删除到$ Processes。您仍然需要填充$ TargetUsers变量。另外:较早的评论指出,鉴于您正在做的事,不需要所有 Prop ,例如:
Get-WmiObject win32_process |
Select-Object @{Name = 'Name '; Expression = { $_.ProcessName -Replace '\.exe$' } },
@{Name = 'User'; Expression = { $_.GetOwner().User} } |
Where-Object{ $TargetUsers -contains $_.User } |
Stop-Process
但是,如果您在代码中执行其他操作,例如记录终止的进程,则建立和维护更多属性相对而言是无害的。仅出于说明目的,也可以通过
ForEach-Object
相对容易地方便进行管道运输,而无需偏离原始对象:Get-WmiObject win32_process |
Where{$TargetUsers -contains $_.GetOwner().User } |
ForEach-Object{ Stop-Process -Id $_.ProcessID }
关于PowerShell的最好的事情之一就是有很多做事的方法。最后一个例子非常简洁,但是添加日志记录或控制台输出之类的东西并不是最佳选择(尽管可行)。另外,西奥(Theo)关于Get-CimInstance也是正确的。如果我没记错的话,
Get-WmiObject
不推荐使用。旧习惯很难打破,因此我所有的示例都使用了Get-WmiObject
。但是,这些概念应适用于整个PowerShell,包括Get-CimInstance
...无论如何,我希望我在这里添加了一些内容。那里有几篇文章讨论了不同的对象创建和操作功能的优缺点等...如果有时间,我将尝试对其进行跟踪。
关于arrays - 将Powershell中的对象重定向到其他功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60347991/