更容易解析';查询用户';在PowerShell中
我当前在PowerShell中有以下查询:更容易解析';查询用户';在PowerShell中,powershell,parsing,scripting,server,powershell-3.0,Powershell,Parsing,Scripting,Server,Powershell 3.0,我当前在PowerShell中有以下查询: query user /server:$server 它返回输出: USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME svc_chthost 2 Disc 1:05 8/16/2016 12:01 PM myusername rdp-t
query user /server:$server
它返回输出:
USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
svc_chthost 2 Disc 1:05 8/16/2016 12:01 PM
myusername rdp-tcp 3 Active . 8/29/2016 11:29 AM
目前,我正在使用@(查询用户/服务器:$server)。将-1
作为一个值来表示登录的用户数(我知道这并不漂亮)。但是,现在我想获得一些信息,如用户名
,ID
,以及登录时间
,以便在脚本的其他部分使用
我的问题围绕着一种更简单的方法来解析上面的信息,或者可能是一种更好的解决方案:计算和收集与登录用户相关的信息
我找到了其他似乎效果更好的解决方案,但我相信一定有更简单的方法来完成这项任务:
$ComputerName | Foreach-object {
$Computer = $_
try
{
$processinfo = @(Get-WmiObject -class win32_process -ComputerName $Computer -EA "Stop")
if ($processinfo)
{
$processinfo | Foreach-Object {$_.GetOwner().User} |
Where-Object {$_ -ne "NETWORK SERVICE" -and $_ -ne "LOCAL SERVICE" -and $_ -ne "SYSTEM"} |
Sort-Object -Unique |
ForEach-Object { New-Object psobject -Property @{Computer=$Computer;LoggedOn=$_} } |
Select-Object Computer,LoggedOn
}#If
}
catch
{
}
评论中有很棒的参考资料,对于这个问题仍然有更多的答案,因为它应该有一个更简单的解决方案
foreach ($s in $servers) #For Each Server
{
foreach($ServerLine in @(query user /server:$s) -split "\n") #Each Server Line
{
#USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
$Parsed_Server = $ServerLine -split '\s+'
$Parsed_Server[1] #USERNAME
$Parsed_Server[2] #SESSIONNAME
$Parsed_Server[3] #ID
$Parsed_Server[4] #STATE
$Parsed_Server[5] #IDLE TIME
$Parsed_Server[6] #LOGON TIME
}
}
这个解决方案现在解决了这个问题,有点草率
对于具有更多功能的更深入的解决方案,检查原始问题的注释:(
< P>),我进一步追加上述代码以正确地格式化,并考虑断开的用户$HaSH = @()
foreach($ServerLine in @(query user) -split "\n") {
$Report = "" | Select-Object UserName, Session, ID, State, IdleTime, LogonTime
$Parsed_Server = $ServerLine -split '\s+'
if($Parsed_Server -like "USERNAME*") {
Continue
}
$Report.UserName = $Parsed_Server[1]
$Report.Session = $Parsed_Server[2]
$Report.ID = $Parsed_Server[3]
$Report.State = $Parsed_Server[4]
$Report.IdleTime = $Parsed_Server[5]
$Report.LogonTime = $Parsed_Server[6]+" " +$Parsed_Server[7]+" "+$Parsed_Server[8]
if($Parsed_Server[3] -eq "Disc") {
$Report.Session = "None"
$Report.ID = $Parsed_Server[2]
$Report.State = $Parsed_Server[3]
$Report.IdleTime = $Parsed_Server[4]
$Report.LogonTime = $Parsed_Server[5]+" " +$Parsed_Server[6]+" "+$Parsed_Server[7]
}
if($Parsed_Server -like ">*") {
$Parsed_Server=$Parsed_Server.Replace(">","")
$Report.UserName = $Parsed_Server[0]
$Report.Session = $Parsed_Server[1]
$Report.ID = $Parsed_Server[2]
$Report.State = $Parsed_Server[3]
$Report.IdleTime = $Parsed_Server[4]
$Report.LogonTime = $Parsed_Server[5]+" " +$Parsed_Server[6]+" "+$Parsed_Server[7]
}
$HaSH+=$Report
}
这是一个老问题,但似乎是一个可行的解决方案:
(query user) -split "\n" -replace '\s\s+', ';' | convertfrom-csv -Delimiter ';'
与上面的答案一样,这会将输出分块成行,但随后会将多个空格字符(\s\s+)替换为分号,然后使用分号作为分隔符从csv转换该输出
之所以有多个空格,是因为列标题中有空格(空闲时间、登录时间),所以只要有一个空格,它就会试图将其解释为多个列。从命令的输出来看,它们似乎总是在项目之间至少保留2个空格,登录时间列的字段中也有空格。用于收集信息
Function Get-QueryUser(){
Param([switch]$Json) # ALLOWS YOU TO RETURN A JSON OBJECT
$HT = @()
$Lines = @(query user).foreach({$(($_) -replace('\s{2,}',','))}) # REPLACES ALL OCCURENCES OF 2 OR MORE SPACES IN A ROW WITH A SINGLE COMMA
$header=$($Lines[0].split(',').trim()) # EXTRACTS THE FIRST ROW FOR ITS HEADER LINE
for($i=1;$i -lt $($Lines.Count);$i++){ # NOTE $i=1 TO SKIP THE HEADER LINE
$Res = "" | Select-Object $header # CREATES AN EMPTY PSCUSTOMOBJECT WITH PRE DEFINED FIELDS
$Line = $($Lines[$i].split(',')).foreach({ $_.trim().trim('>') }) # SPLITS AND THEN TRIMS ANOMALIES
if($Line.count -eq 5) { $Line = @($Line[0],"$($null)",$Line[1],$Line[2],$Line[3],$Line[4] ) } # ACCOUNTS FOR DISCONNECTED SCENARIO
for($x=0;$x -lt $($Line.count);$x++){
$Res.$($header[$x]) = $Line[$x] # DYNAMICALLY ADDS DATA TO $Res
}
$HT += $Res # APPENDS THE LINE OF DATA AS PSCUSTOMOBJECT TO AN ARRAY
Remove-Variable Res # DESTROYS THE LINE OF DATA BY REMOVING THE VARIABLE
}
if($Json) {
$JsonObj = [pscustomobject]@{ $($env:COMPUTERNAME)=$HT } | convertto-json # CREATES ROOT ELEMENT OF COMPUTERNAME AND ADDS THE COMPLETED ARRAY
Return $JsonObj
} else {
Return $HT
}
}
Get-QueryUser
or
Get-QueryUser -Json
基于
我自己的专栏文章。我不确定ID列可以向左延伸多少。不知道末端有多宽。这是一个棘手的问题。也许这样更好: 调用命令示例。如果你用的是鳄梨酱,这很好
$c = get-credential
icm comp1,comp2,comp3 q.ps1 -cr $c | ft
USERNAME SESSIONNAME ID STATE IdleTime LogonTime PSComputerName RunspaceId
-------- ----------- -- ----- -------- --------- -------------- ----------
js1 136 Disc . 6/20/2020 4:26:00 PM comp1 a8e670cd-4f31-4fd0-8cab-8aa11ee75a73
js2 137 Disc . 6/20/2020 4:26:00 PM comp2 a8e670cd-4f31-4fd0-8cab-8aa11ee75a74
js3 138 Disc . 6/20/2020 4:26:00 PM comp3 a8e670cd-4f31-4fd0-8cab-8aa11ee75a75
已编辑:看起来有人已经创建了一个实际运行良好的脚本: 真不敢相信这么多年后,仍然没有本地的PowerShell 我已经修改了Tyler Dickson所做的,并确保结果作为PSCustomObject返回
$Servers = @("10.x.x.x", "10.y.y.y")
$Result = @()
foreach ($Server in $Servers) {
$Lines = @(query user /server:$s) -split "\n"
foreach($Line in $Lines) #Each Server Line
{
if ($Line -match "USERNAME\s+SESSIONNAME\s+ID\s+STATE\s+IDLE TIME\s+LOGON TIME") {
continue # If is the header then skip to next item in array
}
$Parsed_Server = $Line -split '\s+'
$Result += [PSCustomObject]@{
SERVER = $Server
USERNAME = $Parsed_Server[1]
SESSIONNAME = $Parsed_Server[2]
ID = $Parsed_Server[3]
STATE = $Parsed_Server[4]
IDLE_TIME = $Parsed_Server[5]
LOGON_TIME = $Parsed_Server[6]
}
}
}
$Result | Format-Table
示例输出:
SERVER USERNAME SESSIONNAME ID STATE IDLE_TIME LOGON_TIME
------ -------- ----------- -- ----- --------- ----------
10.x.x.x user01 rdp-tcp#13 6 Active . 28/06/2020
10.x.x.x user02 rdp-tcp#35 11 Active 59 29/06/2020
10.y.y.y user03 rdp-tcp#38 12 Active . 29/06/2020
10.y.y.y user04 rdp-tcp#43 14 Active 5 29/06/2020
我觉得有点奇怪,我找不到一个“纯Powershell”的解决方案。其他人花时间创建了一个脚本来完成您想要的任务,不过:也请参见:@poorkenny:一个纯Powershell解决方案可以使用Pinvoke调用WTSQuerySessionInformation(这需要大量代码,特别是对于PS纯粹主义者来说,他将使用.NET中的ModuleBuilder而不是嵌入C代码来创建此脚本).什么是“邋遢”,你想在这里换衣服吗?(试着在一条评论中总结一下,我们不会有太多允许的:-p-现在,我正试图从上面的评论中完善代码)只有
foreach(split){foreach(split){}
正在进行。我相信有一种更简单的方法来解析信息。老实说,这是我能想出的最好的解决方案,不会使解决方案过于复杂化。作为查询用户(quser)已经做了我想做的事情。我主要是在寻找一种管理输出的解决方案。你的解决方案和@Poorkenny的链接都很好,我只是对简短而甜蜜的解决方案很挑剔:P所以我想总结一下,这个解决方案不是太草率-我只是希望有一种更简单的方法将它们分类到可管理的对象中。@TylerDickson如果没有人连接到服务器,我发现你的解决方案有问题(假设是远程计算机),则SESSIONNAME可能为空,从而产生一个“间隙”,导致$Parsed_Server[2]是ID而不是[3];[3]是STATE而不是[4]…您看到解决方案了吗?虽然此代码可以回答此问题,但提供有关此代码为什么和/或如何回答此问题的其他上下文可提高其长期价值。
$Servers = @("10.x.x.x", "10.y.y.y")
$Result = @()
foreach ($Server in $Servers) {
$Lines = @(query user /server:$s) -split "\n"
foreach($Line in $Lines) #Each Server Line
{
if ($Line -match "USERNAME\s+SESSIONNAME\s+ID\s+STATE\s+IDLE TIME\s+LOGON TIME") {
continue # If is the header then skip to next item in array
}
$Parsed_Server = $Line -split '\s+'
$Result += [PSCustomObject]@{
SERVER = $Server
USERNAME = $Parsed_Server[1]
SESSIONNAME = $Parsed_Server[2]
ID = $Parsed_Server[3]
STATE = $Parsed_Server[4]
IDLE_TIME = $Parsed_Server[5]
LOGON_TIME = $Parsed_Server[6]
}
}
}
$Result | Format-Table
SERVER USERNAME SESSIONNAME ID STATE IDLE_TIME LOGON_TIME
------ -------- ----------- -- ----- --------- ----------
10.x.x.x user01 rdp-tcp#13 6 Active . 28/06/2020
10.x.x.x user02 rdp-tcp#35 11 Active 59 29/06/2020
10.y.y.y user03 rdp-tcp#38 12 Active . 29/06/2020
10.y.y.y user04 rdp-tcp#43 14 Active 5 29/06/2020
$result = (&quser) -replace '\s{2,}', ',' | ConvertFrom-Csv | Select -ExpandProperty USERNAME
$loggedinuser = $result.Trim(">")