Regex to match first space only after every word

This topic contains 8 replies, has 4 voices, and was last updated by Profile photo of Curtis Smith Curtis Smith 7 months, 4 weeks ago.

Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #34547
    Profile photo of Mark Hammonds
    Mark Hammonds
    Participant

    Regex to match first space only after every word. I want to replace the first space after every word with a comma so I can pull this into a custom object.

    
    SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE 
     services                                    0  Disc                        
     console                                     3  Conn                        
     rdp-tcp                                 65536  Listen   
    
    #34548
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Something like this should do:

    Edit: Moved code over to a Gist, so the angle brackets wouldn't make our forum software confused.

    #34550
    Profile photo of Mark Hammonds
    Mark Hammonds
    Participant

    damn here is my problem

    I was using '\b\s',',' which worked but the user name column is sometimes blank so I have to add an extra , to shift the columns over when that field is blank

    here is my full command

    $sessions = (QWINSTA /SERVER:$($server.Name) | foreach {(($_.trim() -replace "\b\s",",")) + ","} | ConvertFrom-Csv)

    #34551
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Absolutely nothing regex can do to help you with that. What you could do is parse the header row to figure out roughly where data should be, then start at that column in each data row to figure out where to put the comma.

    Or even better, find a way to work with objects here instead of table-formatted text. Table-formatted is fantastic for humans to read, and absolute crap for computer parsing. (Maybe search for C# example code of how to enumerate TS sessions; that'll be the easiest thing to port over to PowerShell.)

    #34553
    Profile photo of Mark Hammonds
    Mark Hammonds
    Participant

    since session are always numbers and user id's are never numbers this works but I donlt like doing this.. if anyone knows of a better way please chime in

    $sessions = (QWINSTA  /SERVER:$($server.Name) | foreach {(($_.trim() -replace "\b\s",",")) + ","} | ConvertFrom-Csv) 
    Foreach($s in $Sessions){
        if(!($s.USERNAME -like '^a-z')){
            $newsession += New-Object PSObject -Property([Ordered]@{SESSIONNAME = $s.SESSIONNAME; USERNAME = ""; Id = $s.USERNAME; STATE = $s.ID})
        }
    }
    
    #34558
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    I did a search on this and took some code from StackOverflow. The C# / Interop stuff isn't terribly easy to read (and it assumes you're running on a 64-bit OS, which is a little bit sloppy but probably safe these days), but it works. 🙂

    Add-Type -TypeDefinition @'
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace TerminalTools
    {
        public class TermServicesManager
        {
    
            [DllImport("wtsapi32.dll")]
            static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);
    
            [DllImport("wtsapi32.dll")]
            static extern void WTSCloseServer(IntPtr hServer);
    
            [DllImport("Wtsapi32.dll")]
            public static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass,
                out System.IntPtr ppBuffer, out uint pBytesReturned);
    
            [DllImport("wtsapi32.dll")]
            static extern Int32 WTSEnumerateSessions(IntPtr hServer, [MarshalAs(UnmanagedType.U4)] Int32 Reserved,
                [MarshalAs(UnmanagedType.U4)] Int32 Version, ref IntPtr ppSessionInfo, [MarshalAs(UnmanagedType.U4)] ref Int32 pCount);
    
            [DllImport("wtsapi32.dll")]
            static extern void WTSFreeMemory(IntPtr pMemory);
    
            [StructLayout(LayoutKind.Sequential)]
            private struct WTS_SESSION_INFO
            {
                public Int32 SessionID;
                [MarshalAs(UnmanagedType.LPStr)]
                public String pWinStationName;
                public WTS_CONNECTSTATE_CLASS State;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct WTS_CLIENT_ADDRESS
            {
                public uint AddressFamily;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
                public byte[] Address;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct WTS_CLIENT_DISPLAY
            {
                public uint HorizontalResolution;
                public uint VerticalResolution;
                public uint ColorDepth;
            }
    
            public enum WTS_CONNECTSTATE_CLASS
            {
                Active,
                Connected,
                ConnectQuery,
                Shadow,
                Disconnected,
                Idle,
                Listen,
                Reset,
                Down,
                Init
            }
    
            public enum WTS_INFO_CLASS
            {
                InitialProgram = 0,
                ApplicationName = 1,
                WorkingDirectory = 2,
                OEMId = 3,
                SessionId = 4,
                UserName = 5,
                WinStationName = 6,
                DomainName = 7,
                ConnectState = 8,
                ClientBuildNumber = 9,
                ClientName = 10,
                ClientDirectory = 11,
                ClientProductId = 12,
                ClientHardwareId = 13,
                ClientAddress = 14,
                ClientDisplay = 15,
                ClientProtocolType = 16
            }
    
                            private static IntPtr OpenServer(string Name)
        {
            IntPtr server = WTSOpenServer(Name);
            return server;
        }
    
                        private static void CloseServer(IntPtr ServerHandle)
        {
            WTSCloseServer(ServerHandle);
        }
    
            public static List ListSessions(string ServerName)
            {
                IntPtr server = IntPtr.Zero;
                List ret = new List();
                server = OpenServer(ServerName);
    
                try
                {
                    IntPtr ppSessionInfo = IntPtr.Zero;
    
                    Int32 count = 0;
                    Int32 retval = WTSEnumerateSessions(server, 0, 1, ref ppSessionInfo, ref count);
                    Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
    
                    Int64 current = (Int64)ppSessionInfo;
    
                    if (retval != 0)
                    {
                        for (int i = 0; i < count; i++)
                        {
                            WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
                            current += dataSize;
    
                            ret.Add(new TerminalSessionData(si.SessionID, si.State, si.pWinStationName));
                        }
    
                        WTSFreeMemory(ppSessionInfo);
                    }
                }
                finally
                {
                    CloseServer(server);
                }
    
                return ret;
            }
    
            public static TerminalSessionInfo GetSessionInfo(string ServerName, int SessionId)
            {
                IntPtr server = IntPtr.Zero;
                server = OpenServer(ServerName);
                System.IntPtr buffer = IntPtr.Zero;
                uint bytesReturned;
                TerminalSessionInfo data = new TerminalSessionInfo();
    
                try
                {
                    bool worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ApplicationName, out buffer, out bytesReturned);
    
                    if (!worked)
                        return data;
    
                    string strData = Marshal.PtrToStringAnsi(buffer);
                    data.ApplicationName = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientAddress, out buffer, out bytesReturned);
    
                    if (!worked)
                        return data;
    
                    WTS_CLIENT_ADDRESS si = (WTS_CLIENT_ADDRESS)Marshal.PtrToStructure((System.IntPtr)buffer, typeof(WTS_CLIENT_ADDRESS));
                    data.ClientAddress = si;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientBuildNumber, out buffer, out bytesReturned);
    
                    if (!worked)
                        return data;
    
                    int lData = Marshal.ReadInt32(buffer);
                    data.ClientBuildNumber = lData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientDirectory, out buffer, out bytesReturned);
    
                    if (!worked)
                        return data;
    
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.ClientDirectory = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientDisplay, out buffer, out bytesReturned);
    
                    if (!worked)
                        return data;
    
                    WTS_CLIENT_DISPLAY cd = (WTS_CLIENT_DISPLAY)Marshal.PtrToStructure((System.IntPtr)buffer, typeof(WTS_CLIENT_DISPLAY));
                    data.ClientDisplay = cd;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientHardwareId, out buffer, out bytesReturned);
    
                    if (!worked)
                        return data;
    
                    lData = Marshal.ReadInt32(buffer);
                    data.ClientHardwareId = lData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientName, out buffer, out bytesReturned);
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.ClientName = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientProductId, out buffer, out bytesReturned);
                    Int16 intData = Marshal.ReadInt16(buffer);
                    data.ClientProductId = intData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ClientProtocolType, out buffer, out bytesReturned);
                    intData = Marshal.ReadInt16(buffer);
                    data.ClientProtocolType = intData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.ConnectState, out buffer, out bytesReturned);
                    lData = Marshal.ReadInt32(buffer);
                    data.ConnectState = (WTS_CONNECTSTATE_CLASS)Enum.ToObject(typeof(WTS_CONNECTSTATE_CLASS), lData);
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.DomainName, out buffer, out bytesReturned);
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.DomainName = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.InitialProgram, out buffer, out bytesReturned);
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.InitialProgram = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.OEMId, out buffer, out bytesReturned);
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.OEMId = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.SessionId, out buffer, out bytesReturned);
                    lData = Marshal.ReadInt32(buffer);
                    data.SessionId = lData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.UserName, out buffer, out bytesReturned);
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.UserName = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.WinStationName, out buffer, out bytesReturned);
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.WinStationName = strData;
    
                    worked = WTSQuerySessionInformation(server, SessionId,
                        WTS_INFO_CLASS.WorkingDirectory, out buffer, out bytesReturned);
                    strData = Marshal.PtrToStringAnsi(buffer);
                    data.WorkingDirectory = strData;
                }
                finally
                {
                    WTSFreeMemory(buffer);
                    buffer = IntPtr.Zero;
                    CloseServer(server);
                }
    
                return data;
            }
    
        }
    
        public class TerminalSessionData
        {
            public int SessionId;
            public TermServicesManager.WTS_CONNECTSTATE_CLASS ConnectionState;
            public string StationName;
    
                                public TerminalSessionData(int sessionId, TermServicesManager.WTS_CONNECTSTATE_CLASS connState, string stationName)
        {
            SessionId = sessionId;
            ConnectionState = connState;
            StationName = stationName;
        }
    
                        public override string ToString()
        {
            return String.Format("{0} {1} {2}", SessionId, ConnectionState, StationName);
        }
        }
    
        public class TerminalSessionInfo
        {
            public string InitialProgram;
            public string ApplicationName;
            public string WorkingDirectory;
            public string OEMId;
            public int SessionId;
            public string UserName;
            public string WinStationName;
            public string DomainName;
            public TermServicesManager.WTS_CONNECTSTATE_CLASS ConnectState;
            public int ClientBuildNumber;
            public string ClientName;
            public string ClientDirectory;
            public int ClientProductId;
            public int ClientHardwareId;
            public TermServicesManager.WTS_CLIENT_ADDRESS ClientAddress;
            public TermServicesManager.WTS_CLIENT_DISPLAY ClientDisplay;
            public int ClientProtocolType;
        }
    }
    '@
    

    To call the two (ListSessions and GetSessionInfo) that are giving us the info, do something like this:

    [TerminalTools.TermServicesManager]::ListSessions($env:COMPUTERNAME) |
    ForEach-Object { [TerminalTools.TermServicesManager]::GetSessionInfo($env:COMPUTERNAME, $_.SessionId) }
    

    #34564
    Profile photo of Mark Hammonds
    Mark Hammonds
    Participant

    damn lol

    #34571
    Profile photo of Bob McCoy
    Bob McCoy
    Participant

    Just curious ...
    If this is specifically for Terminal Services, have you looked at the module out on Codeplex? It will save you a lot of time.
    http://psterminalservices.codeplex.com/

    #34589
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    I played with this just for fun. Since this is fixed width output, I figured I would just use the column header widths to determine column width and use that for the data widths.

    $data = qwinsta
    $result = $data[0] -match "(?'Column1'.+?\s+)(?'Column2'.+?\s+)(?'Column3'.+?\s+)(?'Column4'.+?\s+)(?'Column5'.+?\s+)(?'Column6'.+)"
    $headers = $Matches
    $data | Select-Object -Skip 1 | ForEach-Object {
        $result = $_ -match "(?'Column1'.{$(($headers['Column1']).Length)})(?'Column2'.{$(($headers['Column2']).Length)})(?'Column3'.{$(($headers['Column3']).Length)})(?'Column4'.{$(($headers['Column4']).Length)})(?'Column5'.{$(($headers['Column5']).Length)})(?'Column6'.{$(($headers['Column6']).Length)})"
        [PSCustomObject]@{
            ($headers['Column1']).trim() = ($matches['Column1']).substring(1).trim()
            ($headers['Column2']).trim() = ($matches['Column2']).trim()
            ($headers['Column3']).trim() = ($matches['Column3']).trim()
            ($headers['Column4']).trim() = ($matches['Column4']).trim()
            ($headers['Column5']).trim() = ($matches['Column5']).trim()
            ($headers['Column6']).trim() = ($matches['Column6']).trim()
        }
    }
    

    This first appeared to work well, but then I noticed that the ID column appears to be right aligned. This makes the column header width not necessarily match the data.

    Results:

    SESSIONNAME USERNAME ID STATE  TYPE DEVICE
    ----------- -------- -- -----  ---- ------
    services             0  Disc              
    console              3  Conn              
    rdp-tcp     655      36 Listen            
    

    Further testing of qwinsta; however, appears to prove that the output is always the same fixed width, not adjusted fixed width based on values. If that continues to be the case, using regex to match the fixed with values is simple enough.

    qwinsta | Select-Object -Skip 1 | ForEach-Object {
        $result = $_ -match ".(?'sessionname'.{16})\s\s(?'username'.{20})\s\s(?'id'.{5})\s\s(?'state'.{6})\s\s(?'type'.{10})\s\s(?'device'.{7})"
        [PSCustomObject]@{
            SESSIONNAME = ($matches['sessionname']).trim()
            USERNAME = ($matches['username']).trim()
            ID = ($matches['id']).trim()
            STATE = ($matches['state']).trim()
            TYPE = ($matches['type']).trim()
            DEVICE = ($matches['device']).trim()
        }
    }
    

    Results

    SESSIONNAME USERNAME ID    STATE  TYPE DEVICE
    ----------- -------- --    -----  ---- ------
    services             0     Disc              
    console              3     Conn              
    rdp-tcp              65536 Listen            
    

    This can also be done using substring if the width is fixed.

    qwinsta | Select-Object -Skip 1 | ForEach-Object {
        [PSCustomObject]@{
            SESSIONNAME = $_.substring(1,17).trim()
            USERNAME = $_.substring(19,21).trim()
            ID = $_.substring(41,5).trim()
            STATE = $_.substring(48,7).trim()
            TYPE = $_.substring(56,11).trim()
            DEVICE = $_.substring(68).trim()
        }
    }
    

    Results:

    SESSIONNAME USERNAME ID    STATE  TYPE DEVICE
    ----------- -------- --    -----  ---- ------
    services             0     Disc              
    console              3     Conn              
    rdp-tcp              65536 Listen            
    

    For purposes of speed, the substring method runs slightly faster on average with a 10,001 sample measurement.
    RegEx method (1.39082208779123 milliseconds on average)
    Substring method (1.24752035796421 milliseconds on average)

Viewing 9 posts - 1 through 9 (of 9 total)

You must be logged in to reply to this topic.