I\'m a PowerShell novice (Bash is my thing normally) who\'s currently trying to obtain qwinsta output to show who is logged in as an \'rdpwd\' (rdesktop) user so that I can
Honestly I'd look up a better way to do this, but you can fudge it with some text manipulation and the ConvertFrom-Csv
cmdlet:
$(qwinsta.exe) -replace "^[\s>]" , "" -replace "\s+" , "," | ConvertFrom-Csv | select username
Firstly replace any leading spaces or >
characters with nothing, then replace any white spaces with a comma. Then you can pipe to ConvertFrom-Csv
and work with the data as an object.
Actually, the above has some issues, mostly with the \s+
because if a column is blank it does not get correctly recognised as a blank field, and the next text is incorrectly promoted to the current field.
The below is a full blown parser for this command, and would probably work for any sort of tabulated output from a native windows exe:
$o = @()
$op = $(qwinsta.exe)
$ma = $op[0] | Select-String "(?:[\s](\w+))" -AllMatches
$ErrorActionPreference = "Stop"
for($j=1; $j -lt $op.length; $j++) {
$i = 0
$obj = new-object pscustomobject
while ($i -lt $ma.matches.count) {
$prop = $ma.matches[$i].groups[1].value;
$substrStart = $ma.matches[$i].index
$substrLen = $ma.matches[$i+1].index - $substrStart
try {
$obj | Add-Member $prop -notepropertyvalue $op[$j].substring($substrStart,$substrLen).trim()
}
catch [ArgumentOutOfRangeException] {
$substrLen = $op[$j].length - $substrStart
if($substrLen -gt 0) {
$obj | Add-Member $prop -notepropertyvalue $op[$j].substring($substrStart,$substrLen).trim()
}
else {
$obj | Add-Member $prop -notepropertyvalue ""
}
}
$i++
}
$o += ,$obj
}
$o | ? { $_.type -eq 'rdpwd'} | select username
USERNAME
--------
user.name1
user.name2
user.name3
Can't tell for sure, but it sounds like you're trying to do a regex split using the string .split()
method. That doesn't work. Use the Powershell -split
operator to do a regex split:
(@'
SESSIONNAME USERNAME ID STATE TYPE DEVICE
services 0 Disc
console 1 Conn
rdp-tcp#0 user.name1 2 Active rdpwd
rdp-tcp#1 user.name2 3 Active rdpwd
rdp-tcp#1 user.name3 4 Active rdpwd
rdp-tcp 65536 Liste
'@).split("`n") |
foreach {$_.trim()} | sv x
$x -match 'rdpwd' |
foreach { ($_ -split '\s+')[1] }
user.name1
user.name2
user.name3
a simple way
get list of active users only
$logonusers = qwinsta /server:ts33 | Out-String -Stream | Select-String "Active"
clears all the info up apart from the users, with -replace command
$logonusers = $logonusers -replace("rdp-tcp") -replace("Active") -
replace("rdpwd") -replace("#") -replace '\s+', ' ' -replace '[0-9]',' '
$logonusers
will then list all the active users.
I wrote a reusable ConvertFrom-SourceTable cmdlet which is available for download at the PowerShell Gallery and the source code from the GitHub iRon7/ConvertFrom-SourceTable repository.
$Object = ConvertFrom-SourceTable '
SESSIONNAME USERNAME ID STATE TYPE DEVICE
services 0 Disc
console 1 Conn
rdp-tcp#0 user.name1 2 Active rdpwd
rdp-tcp#1 user.name2 3 Active rdpwd
rdp-tcp#1 user.name3 4 Active rdpwd
rdp-tcp 65536 Listen
'
It pretty flexible and capable of reading a lot of table format including reading the output of the results. Or even if e.g. the ID
column is right aligned meaning that it would concern integers rather than strings:
$Object = ConvertFrom-SourceTable '
ID TYPE USERNAME STATE DEVICE SESSIONNAME
-- ---- -------- ----- ------ -----------
0 Disc services
1 Conn console
2 rdpwd user.name1 Active rdp-tcp#0
3 rdpwd user.name2 Active rdp-tcp#1
4 rdpwd user.name3 Active rdp-tcp#1
65536 Listen rdp-tcp
'
For details see: ConvertFrom-SourceTable -?
Print field 4,5 and 6 in second column.
awk 'NR>3&&NR<7{print $2}' file
user.name1
user.name2
user.name3
I like Matt's answer for this, however it has issues with spaces in column headings (they are problematic in general, but sometimes you can't do much). Here's a tweaked, functionized version to help. Note you could probably tweak the preproc to include e.g. tabs or other delimiters but still relies on per-line indexes being constant.
function Convert-TextColumnsToObject([String]$data)
{
$splitLinesOn=[Environment]::NewLine
$columnPreproc="\s{2,}"
$headerString = $data.Split($splitLinesOn) | select -f 1
#Preprocess to handle headings with spaces
$headerElements = ($headerString -replace "$columnPreproc", "|") -split "\|" | Where-Object{$_}
$headerIndexes = $headerElements | ForEach-Object{$headerString.IndexOf($_)}
$results = $data.Split($splitLinesOn) | Select-Object -Skip 1 | ForEach-Object{
$props = @{}
$line = $_
For($indexStep = 0; $indexStep -le $headerIndexes.Count - 1; $indexStep++){
$value = $null # Assume a null value
$valueLength = $headerIndexes[$indexStep + 1] - $headerIndexes[$indexStep]
$valueStart = $headerIndexes[$indexStep]
If(($valueLength -gt 0) -and (($valueStart + $valueLength) -lt $line.Length)){
$value = ($line.Substring($valueStart,$valueLength)).Trim()
} ElseIf ($valueStart -lt $line.Length){
$value = ($line.Substring($valueStart)).Trim()
}
$props.($headerElements[$indexStep]) = $value
}
[pscustomobject]$props
}
return $results
}
Example:
$data= @"
DRIVER VOLUME NAME
local 004e9c5f2ecf96345297965d3f98e24f7a6a69f5c848096e81f3d5ba4cb60f1e
local 081211bd5d09c23f8ed60fe63386291a0cf452261b8be86fc154b431280c0c11
local 112be82400a10456da2e721a07389f21b4e88744f64d9a1bd8ff2379f54a0d28
"@
$obj=Convert-TextColumnsToObject $data
$obj | ?{ $_."VOLUME NAME" -match "112be" }