How to trim string until first capital letter

Viewed 167

Here is my issue. If I type $env:UserName, the output is JoeDow What I need is to trim this string from the end until the first capital character. The output should be JoeD I tried everything and had no luck Thanks in advance M

3 Answers

RegEx is amazing, but simple in this case:

$String = 'JoeDow'
$String -creplace '[a-z]+$'

This simply says case sensitively replace any lowercase character from a-z that's at the end of the string.

In your case:

$env:USERNAME -creplace '[a-z]+$'

Note: There are lots of ways I can think of doing this. The particular approach above assumes there's 2 Capital letters and we want to chop only the lowercase letters after the last capital letter. If you give it a string like 'Joe' it will return "J", because it'll replace the trailing lowercase letters just the same. Therefore if this is not a super-consistent scenario we'd likely have to go another route.

Update, Building on Sage's nice example

If you use .LastIndexOfAny(), again with assurance the format is stable, you can nail it in very few lines:

$String = "JoeDow"
$CapitalLetters = [Char[]](65..90)
$String.Substring(0, ($String.LastIndexOfAny($CapitalLetters) +1 ))

This relies on the last capital not the second capital. Again, it depends on how reliable the pattern your expecting is.

Another RegEx Approach:

$String = "JoeDow"
$String = -join ($String -csplit "([A-Z])")[1..3]
$String

This one uses capital letters as the delimiter, however the parens instructs the -csplit operator to return the delimiter in the array. So, by picking the correct range we can be sure to grab "J" + "oe" + "D". Unlike the other examples this uses the 2nd capital, not necessarily the last. While the other chose the last which was not necessarily the 2nd.

You can easily get an array of all the capital letters in Powershell with [char[]](65..90)

From there, using .IndexOfAny method, you can gather the position of the second capital letter and use the substring method to select the string from its beginning to the second capital letter exclusively.

That would look like this.


Function Get-TrimmedName($Name) {
    $CapitalLetters = [char[]](65..90)


    $SecondCapEndIndex = $Name.IndexOfAny($CapitalLetters, 1) + 1
    $TrimmedName = $Name 
    if ($SecondCapEndIndex -gt 0 -and $SecondCapEndIndex -lt $Name.Length) { 
        $TrimmedName = $Name.Substring(0, [int]$SecondCapEndIndex)
    }
    return $TrimmedName
}

Get-TrimmedName -Name 'JoeDow' # returns JoeD
Get-TrimmedName -Name 'MaxD' # returns MaxD
Get-TrimmedName -Name 'Max' # returns Max
Get-TrimmedName -Name 'PatrickDesjardins' # returns PatrickD

Now, just because fun needed to be had, here is a more complex version that will go futher and work with more complex name such as JohnDoeDawson (Would return JohnDD), allow you to get initials out of it (eg: JohnDoeDawson = JDD) and even convert names to Title cases on the fly (eg: "john doe dawson" = JohnDD)


<#
.SYNOPSIS
Get the trimmed version of a name.

.DESCRIPTION
Returns the complete first name and the trimmed down last names (First letter only)

.PARAMETER Name
Name to be trimmed.

.PARAMETER Initials
If set, only the initials will be returned.

.PARAMETER TitleCase
If set, the name passed down will be converted to TitleCase first. It is good to be noted that names with no
spaces will loose any capitals after the First letter (eg: JohnDoe -> Johndoe) so it is best used when the name is formatted
with spaces.)

.EXAMPLE
An example

.NOTES
General notes
#>
Function Get-TrimmedName([String]$Name, [Switch]$Initials, [Switch]$TitleCase) {
    $CapitalLetters = [char[]](65..90)


    $Index = 0
    $output = [System.Text.StringBuilder]::new()

    if ($TitleCase) {
        $Ti = [cultureinfo]::CurrentCulture.TextInfo
        $Name = $Ti.ToTitleCase($Name)
    }

    $MyCapLetters =
    while ($true) {
        $Index = $Name.IndexOfAny($CapitalLetters, $Index)
        if ($Index -eq -1) { break }
        $Index
        $Index += 1

    }

    if ($Initials) {
        if ($MyCapLetters.Count -eq 0) {
            return $Name[0]
        }
        Foreach ($C in $MyCapLetters) { $Output.Append($Name.Substring($C, 1)) | Out-Null }
        return $Output.ToString()
    }

    $MyCapLetters = $MyCapLetters.Where( { $_ -gt 0 })
    if ($MyCapLetters.Count -gt 0) {
        $output.Append($Name.Substring(0, $MyCapLetters[0] + 1)) | Out-Null
    }
    else {
        return $Name
    }
    foreach ($C in $MyCapLetters | Select-Object -Skip 1) { 
        $output.Append($Name.Substring($C, 1)) | Out-Null 
    }
    return $output.ToString()
}

Now some examples of this more complex function


#Base example
Get-TrimmedName -Name JohnDoe  # Returns JohnD
# Works with complex names
get-trimmedname 'PatrickDesjardinsPowell' # returns PatrickDP
#Initials only
get-trimmedname 'PatrickDesjardinsPowell' -Initials # Returns PDP

#TitleCase - if the name contains spaces
get-trimmedname -name "John doe"  -TitleCase #Returns JohnD
get-trimmedname -name "john doe"  -TitleCase #Returns JohnD

# Do not use titlecase if the name is in 1 word
#  TitleCase behavior is to find word boundaries so "jOHNDoe" would get converted to Johndoe since
# considered as a single word.
get-trimmedname -name "JohnDoe"  -titlecase #Returns Johndoe

References

MSDN - .Net String.IndexOfAny Method

MSDN - .Net String.Substring Medhod

MSDN - Everything about arrays - Powershell - Special Index tricks

OK, I need to add some more information. I'm going to run this script on the remote machine via Intune. The problem is if I put a full email address in the script - all good, but if I use a variable like $env:username it pickup system credentials, and the script failed. Is any way to get a username on the remote machine using Intune? It will be executed on a couple of computers. Thanks

Related