We just wrapped up the 2022 edition of the PowerShell+DevOps Global Summit. It was terrific to be with passionate PowerShell professionals again. The culmination of the event is the Iron Scripter Challenge. You can learn more about this year's event and winner here. But there is more to the Iron Scripter than this event. Throughout the year, you can find scripting challenges to test your PowerShell skill at https://ironscripter.us. You are encouraged to share links to your solutions in the comments. Today, I have my solutions for a recent challenge.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
The warm-up challenge was all about manipulating strings. That alone may not sound like much. But the process of discovering how to do it and wrapping it up in a PowerShell function is where the true value lies.
Beginner Level: Write PowerShell code to take a string like 'PowerShell' and display it in reverse.
Intermediate Level: Take a sentence like, "This is how you can improve your PowerShell skills." and write PowerShell code to display the entire sentence in reverse with each word also reversed. You should be able to encode and decode text. Ideally, your functions should take pipeline input. For bonus points, toggle upper and lower case when reversing the word.
Reversing Text
PowerShell treats every string of text as an array.
PS C:\> $w = "PowerShell"
PS C:\> $w[0]
P
Using the range operator, I can get elements in reverse.
PS C:\> $w[-1..-($w.length)]
l
l
e
h
S
r
e
w
o
P
Use the Join operator to put the characters back together again.
PS C:\> $w[-1..-($w.length)] -join ''
llehSrewoP
With this core concept validated, it is simple enough to wrap it in a function.
Function Invoke-ReverseWord {
[cmdletbinding()]
[OutputType("string")]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
HelpMessage = "Enter a word."
)]
[ValidateNotNullOrEmpty()]
[string]$Word
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Word"
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Word length is $($Word.length)"
#get the characters in reverse and join into a string
($Word[-1.. -($Word.length)]) -join ''
} #process
End {
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
}
Reversing a Sentence
Reversing words in a sentence follows the same principle. I can split the sentence into an array of words and then access the elements in reverse.
PS C:\> $t = "This is how you can improve your PowerShell skills"
PS C:\> $words = $t.split()
PS C:\> $words[-1..-($words.count)]
skills
PowerShell
your
improve
can
you
how
is
This
Again, I can join the words with a space. If I wanted to, I could reverse each word as well. That's what this function does.
Function Invoke-ReverseText {
[CmdletBinding()]
[OutputType("string")]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
HelpMessage = "Enter a phrase."
)]
[ValidateNotNullOrEmpty()]
[ValidatePattern("\s")]
[string]$Text
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Text"
#split the phrase on white spaces and reverse each word
$words = $Text.split() | Invoke-ReverseWord
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Reversed $($words.count) words"
($words[-1.. - $($words.count)]) -join " "
} #process
End {
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
}
PS C:\> Invoke-ReverseText "This is how you can improve your PowerShell skills"
slliks llehSrewoP ruoy evorpmi nac uoy woh si sihT
PS C:\> Invoke-ReverseText "This is how you can improve your PowerShell skills" | invoke-reversetext
This is how you can improve your PowerShell skills
PS C:\>
Toggle Case
Figuring out how to toggle case is a bit trickier. Each letter in a word is technically a [Char] type object.
PS C:\> $t = "PowerShell"
PS C:\> $t[0]
P
PS C:\> $t[0].gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Char System.ValueType
[Char] objects have a numeric value.
PS C:\> $t[0] -as [int]
80
On my en-US system, upper-case alphabet characters fall within the range of 65-90. Lower-case letters are 97-112.
PS C:\> [char]80
P
PS C:\> 100 -as [char]
d
To toggle case, I need to get the numeric value of each character and then invoke the toLower() or toUpper() method as required.
Function Invoke-ToggleCase {
[cmdletbinding()]
[OutputType("String")]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
HelpMessage = "Enter a word."
)]
[ValidateNotNullOrEmpty()]
[string]$Word
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Word"
$Toggled = $Word.ToCharArray() | ForEach-Object {
$i = $_ -as [int]
if ($i -ge 65 -AND $i -le 90) {
#toggle lower
$_.ToString().ToLower()
}
elseif ($i -ge 97 -AND $i -le 122) {
#toggle upper
$_.ToString().ToUpper()
}
else {
$_.ToString()
}
} #foreach-object
#write the new word to the pipeline
$toggled -join ''
} #process
End {
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
}
PS C:\> invoke-togglecase $t
pOWERsHELL
Putting It All Together
Here's a function that does it all.
Function Invoke-ReverseTextToggle {
[CmdletBinding()]
[OutputType("string")]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
HelpMessage = "Enter a phrase."
)]
[ValidateNotNullOrEmpty()]
[ValidatePattern("\s")]
[string]$Text
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Text"
#split the phrase on white spaces and reverse each word
$words = $Text.split() | Invoke-ToggleCase | Invoke-ReverseWord
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Reversed $($words.count) words"
($words[-1.. - $($words.count)]) -join " "
} #process
End {
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
}
PS C:\> Invoke-ReverseTextToggle "This is how YOU can improve your PowerShell skills"
SLLIKS LLEHsREWOp RUOY EVORPMI NAC uoy WOH SI SIHt
PS C:\>
PS C:\> Invoke-ReverseTextToggle "This is how YOU can improve your PowerShell skills" | Invoke-ReverseTextToggle
This is how YOU can improve your PowerShell skills
After putting that together, I realized someone might not want all of the transformations. Maybe they only want to toggle case and reverse words. Here's a function that offers flexibility.
Function Convert-Text {
<#
This function relies on some of the previous functions or the functions could be nested
inside the Begin block
Order of operation:
toggle case
reverse word
reverse text
#>
[cmdletbinding()]
Param(
[Parameter(Position = 0, Mandatory, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string]$Text,
[Parameter(HelpMessage = "Reverse each word of text")]
[switch]$ReverseWord,
[Parameter(HelpMessage = "Toggle the case of the text")]
[switch]$ToggleCase,
[Parameter(HelpMessage = "Reverse the entire string of text")]
[switch]$ReverseText
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Converting $Text"
$words = $text.Split()
if ($ToggleCase) {
Write-Verbose "toggling case"
$words = $words | Invoke-ToggleCase
}
If ($reverseWord) {
Write-Verbose "reversing words"
$words = $words | Invoke-ReverseWord
}
if ($ReverseText) {
Write-Verbose "reversing text"
$words = ($words[-1.. - $($words.count)])
}
#write the converted text to the pipeline
$words -join " "
} #process
End {
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
} #close Convert-Text
As written, this function relies on some of the earlier functions. But now, the user has options.
PS C:\> convert-text "I AM an Iron Scripter!" -ToggleCase
i am AN iRON sCRIPTER!
PS C:\>
PS C:\> convert-text "I AM an Iron Scripter!" -ReverseWord
I MA na norI !retpircS
PS C:\>
PS C:\> convert-text "I AM an Iron Scripter!" -ReverseWord -ToggleCase
i ma NA NORi !RETPIRCs
PS C:\>
PS C:\> convert-text "I AM an Iron Scripter!" -Reversetext -ToggleCase -ReverseWord
!RETPIRCs NORi NA ma i
PS C:\>
PS C:\> convert-text "I AM an Iron Scripter!" -Reversetext -ToggleCase -ReverseWord | convert-text -ReverseWord -ToggleCase -ReverseText
I AM an Iron Scripter!
Challenge Yourself
If you have any questions about my code samples, please feel free to leave a comment. I also want to emphasize that my solutions are not the only way to achieve these results. There is value in comparing your work with others. I strongly encourage you to watch for future Iron Scripter challenges and tackle as much as possible. However, you don't have to wait. There are plenty of existing challenges on the site for all PowerShell scripting levels. Comments may be closed, so you can't share your solution, but that shouldn't prevent you from ramping your skills.
3 thoughts on “An Iron Scripter Warm-Up Solution”
Comments are closed.