Split text by columns in PowerShell

后端 未结 12 2085
猫巷女王i
猫巷女王i 2020-11-28 15:37

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

相关标签:
12条回答
  • 2020-11-28 15:57

    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.

    EDIT

    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
    
    0 讨论(0)
  • 2020-11-28 15:58

    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
    
    0 讨论(0)
  • 2020-11-28 16:01

    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.

    0 讨论(0)
  • 2020-11-28 16:03

    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 -?

    0 讨论(0)
  • 2020-11-28 16:04

    Print field 4,5 and 6 in second column.

    awk 'NR>3&&NR<7{print $2}' file
    
        user.name1
        user.name2
        user.name3
    
    0 讨论(0)
  • 2020-11-28 16:07

    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" }
    
    0 讨论(0)
提交回复
热议问题