Tag Archives: join

Silly Saturday PowerShell Palindromes

242px-Sator_Square_at_OppèdeNormally I post amusing PowerShell-related content on Fridays as part of my Friday Fun series. These are light-hearted articles about using PowerShell. Almost always they are not practical but they serve as a learning vehicle. My topic this week seems extra silly so I’m moving it to Saturday. I’m a pushover for alliteration.

Last week I came across a comment on Facebook that involved palindromes. A palindrome is a word that is spelled the same backwards and forwards. Names like Otto and ABBA are palindromes. So are words like madam, civic, and redder. I thought it would be fun to use PowerShell to test if a string was a palindrome. To figure this out I would need to find the midpoint of the string, get the first part of the string and then compare it to the last part of the string, but in reverse order.


PS C:\> $t = "madam"
PS C:\> $mid=[math]::Truncate($t.length/2)
PS C:\> $mid
2

I’m using the Truncate method from the [Math] class so that when I divide the string length by 2, the value is rounded down. This will be important in a moment. Getting the first half of the string is pretty simple.


PS C:\> $start = -join ($t[0..($mid-1)])
PS C:\> $start
ma

I’m getting characters in $t by their index number. In this case going from 0 to the midpoint minus 1. This would give me an array of characters so I regroup them using the -join operator. As an alternative, I could also have done this:


PS C:\> $start = $t.Substring(0,($mid))

Now to get the last half. Remember, I need to get the end of the string in reverse order until I reach the midpoint again. I can use the same technique I used for the front, except this time starting at the end and going backwards.


PS C:\> $end = -join ($t[-1..-($mid)])
PS C:\> $end
ma

Now to compare $start and $end.


PS C:\> $start -eq $end
True

You may be wondering, “What about the ‘d’?” Well, in this case it is irrelevant because going backwards or forwards the ‘d’ is in the same place. I think of these as pivot letters. But this technique also works for strings without a pivot point.


PS C:\> $t="redder"
PS C:\> $mid=[math]::Truncate($t.length/2)
PS C:\> $start = -join ($t[0..($mid-1)])
PS C:\> $end = -join ($t[-1..-($mid)])
PS C:\> $start -eq $end
True
PS C:\> $start
red
PS C:\> $end
red
PS C:\>

So how about a simple function to test if a string is a palindrome?


Function Test-IsPalindrome {
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
[ValidateNotNullorEmpty()]
[string]$Text,
[switch]$IgnoreSpace
)

Process {
Write-Verbose "Testing $Text"
if ($IgnoreSpace) {
Write-Verbose "Removing spaces from text"
$text = $text.Replace(" ","")
Write-Verbose $text
}
$l = $text.length
Write-Verbose "Length is $l"
$mid = [math]::Truncate($l/2)
Write-Verbose "Midpoint is $mid"

#could also use Substring()
$start = -join ($text[0..($mid-1)]) #$text.Substring(0,($mid))
$end = -join ($text[-1..-($mid)])
Write-Verbose "Start: $start"
Write-Verbose "Pivot: $($Text[$mid])"
Write-Verbose "End : $end"
if ($start -eq $end) {
$True
}
else {
$false
}

} #process
} #end Test-IsPalindrome

This is an advanced function so I could add some verbose messages, which is great for troubleshooting and debugging. I also wanted to be able to pipe strings to the function. The intent is to test a string and return True or False. The code is essentially the same thing I showed you, except now it is a little easier to use.


PS C:\> "redder","foo","civic","madam" | Test-IsPalindrome

True
False
True
True

I added an extra feature to strip out spaces.


PS C:\> Test-IsPalindrome "Madam Im Adam" -IgnoreSpace -Verbose
VERBOSE: Testing Madam Im Adam
VERBOSE: Removing spaces from text
VERBOSE: MadamImAdam
VERBOSE: Length is 11
VERBOSE: Midpoint is 5
VERBOSE: Start: Madam
VERBOSE: Pivot: I
VERBOSE: End : madAm
True
PS C:\> Test-IsPalindrome "A nut for a jar of tuna" -IgnoreSpace
True

Yes, this is a silly application of PowerShell. But I hope it gives you an idea of how to use the -Join operator and to work with strings. By the way, I could use these techniques to turn a string into a palindrome.


PS C:\> $text="sum"
PS C:\> -join ($Text, -join $Text[-1..-($Text.length)])
summus

Ok, perhaps not a valid word in English but see if you can follow what the -Join operators are doing and how this snippet works. If you’d like to play with this, you can download Test-IsPalindrome.

Friday Fun PowerShell Crypto

I’m a big fan of codes, ciphers and secret messages. I obviously am a big PowerShell fan as well. So why not mash these things together? Today’s Friday Fun is a PowerShell module I call PSCode. The module contains a few functions for encoding and decoding text. Now, before you get too excited, these won’t stand up to NSA scrutiny or even anyone proficient at code breaking, although I’d like to think I’ve made it a little challenging. Of course, the whole point of these Friday Fun posts is to learn a little PowerShell as well.

The basic encoding premise I’m using is a relative simple transposition. That is, replace S with E. But how to figure out what character substitute? There needs to be some “key” so that the message can later be decoded. My approach was to turn plaintext into its corresponding series of [CHAR] objects. I could have simply used the ToCharArray() method, but PowerShell is too helpful and I wanted to get the underlying integer value. My approach was to process each character of the word with a For statement.


for ($i=0;$i -lt $word.length;$i++) {
$x=$word[$i]
#get the current numeric value
$x=[char]::ConvertToUtf32("$x",0)

The trick here is to use the ConvertToUTF32() method of the [CHAR] class to get the integer value. From the console this is what it looks like:


PS S:\> [char]::ConvertToUtf32("P",0)
80

My substitution idea is to get another character based on some calculated offset. I could have simply said always get the current value plus 5. But then frequency analysis would break that pretty quickly. So my default algorithm, which can be modified via a parameter, is to calculate an offset based on the length of the current word. I take this value and perform a modulo operation using pi, adding 3 to it and rounding it to an integer. This guarantees an offset.


PS S:\> $word="PowerShell"
PS S:\> $word.length%[math]::pi
0.575222039230621
PS S:\> $word.length%[math]::pi+3
3.57522203923062
PS S:\> [int]$off=$word.length%[math]::pi+3
PS S:\> $off
4

This value is added to the [CHAR] value of the current letter. So in my example, the new value is 84. To get the corresponding character I invoke the ConvertFromUtf32() method and write the result to the pipeline.


PS S:\> [char]::ConvertFromUtf32(84)
T

Because the offset is based on a per word length, it varies throughout the plaintext. Thus each word would require a separate analysis, at least for most unclassified civilians. Anyway, the last piece is to use the -Join operator to assemble each character back into a word and write it to the pipeline. Here’s the ConvertTo-PSCode function.


Function ConvertTo-PSCode {
<#
comment based help
#>

[cmdletbinding()]

Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
[AllowEmptyString()]
[AllowNull()]
[string[]]$Text,
[ValidateNotNullorEmpty()]
[scriptblock]$Scriptblock={($word.length%[math]::pi)+3}
)

Begin {
#an array to hold encoded output
Write-Verbose "Starting $($myinvocation.mycommand)"
$Out=@()
}
Process {

#split text into words and lines
foreach ($line in $text) {
$newLine=""
Write-Verbose "Processing line: $line"
$NewLine+= foreach ($word in ($line.Split())) {
write-Verbose " Processing word: $word"
$chars = for ($i=0;$i -lt $word.length;$i++) {
$x=$word[$i]
#get the current numeric value
$x=[char]::ConvertToUtf32("$x",0)
#Calculate an offset. The default is the length of the
#word modulo pi rounded to an integer plus 3
[int]$off=&$Scriptblock
Write-Verbose "Using an offset of $off based on length $($word.length)"
[int]$y=$x+$off
#get the new value
[char]::ConvertFromUtf32($y)
} #for
#write the new "word" to the pipeline
$NewWord=$chars -join ""
$NewWord
} #foreach word
$Out+=$NewLine
} #foreach line
} #end process
End {
Write $Out
}
} #function

The function takes plaintext as a parameter or you can pipe to it. The offset algorithm is passed as a scriptblock. You can pass your own which I’ll show you in a moment.


PS S:\> convertto-pscode "I am the walrus"
M fr znk }grx{y
PS S:\> "I am the walrus" | convertto-pscode
M fr znk }grx{y

To decode the text, I can use the same technique. Except instead of adding the offset, I subtract it.


#rounded to an integer
[int]$off=&$Scriptblock
Write-Verbose "Using an offset of $off based on length $($word.length)"
[int]$y=$x-$off
#get the new value
[char]::ConvertFromUtf32($y)

The ConvertFrom-PSCode function also takes pipelined input which makes it very easy to test.


PS S:\> "I am the walrus" | convertto-pscode | convertfrom-pscode
I am the walrus

If I decide to use a different algorithm, I need to specify it for both functions. Depending on your formula you might end up with non-alpha characters.

As you can see in the figure the offset is the length of the word plus 5 which makes for some interesting code. Now for the really cool part. Because the convert functions write to the pipeline you can direct them to Out-File to save your results. Let’s say I have a simple script.


#requires -version 2.0

[cmdletbinding()]

Param(
[Parameter(Position=0)]
[ValidateNotNullorEmpty()]
[string]$name="m*"
)

write-host "$(Get-Date) Starting a sample script" -ForegroundColor Green
Write-verbose "getting services where name = $name"
Get-service -name $name | where {$_.status -eq "running"}

write-host "$(Get-Date) Ending a sample script" -ForegroundColor Green

I’ll encode the script using the defaults.


PS S:\> cat c:\work\abc.txt | convertto-pscode | out-file c:\work\secretscript.txt
PS S:\> cat C:\work\secretscript.txt
)xkw{oxky 2{jwxnts 846

_gqhpixfmrhmrk,-a

Vgxgs.
_Teveqixiv,TswmxmsrA4-a
_ZepmhexiRsxRyppsvIqtx},-a
`xywnslb)sfrjB'r/'
-

{vmxi1lswx (*.Mkz3Jgzk/ Xyfwynsl e ygsvrk wgvmtx& 0IruhjurxqgFroru Lwjjs
Zulwh0yhuervh 'ljyynsl xjw{nhjx |mjwj reqi A *tgsk(
Ljy2xjw{nhj 2sfrj )sfrj € |mjwj (c2wxexyw 3kw &vyrrmrk&

{vmxi1lswx (*.Mkz3Jgzk/ Ktjotm e ygsvrk wgvmtx& 0IruhjurxqgFroru Lwjjs

What I thought would be extra fun would be a way to run this encoded script. So I wrote Invoke-PSCode.


Function Invoke-PSCode {

<#
comment based help
#>

[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
[AllowEmptyString()]
[AllowNull()]
[string[]]$Text,
[ValidateNotNullorEmpty()]
[scriptblock]$Scriptblock={($word.length%[math]::pi)+3},
[hashtable]$Arguments
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
#define an empty string to hold the contents of the decoded command
$a=""
}
Process {
foreach ($item in $text) {
#add each decoded line to the variable plus a line return
Write-Verbose "Decoding $item"
$a+="{0} `n" -f (ConvertFrom-PSCode -Text $item -Scriptblock $Scriptblock)
}
} #process

End {
Write-Verbose "Running command"
#create a scriptblock
$myCommand=$ExecutionContext.InvokeCommand.NewScriptBlock($a)
#splat the arguments to the script block
&$myCommand @Arguments
Write-Verbose "Ending $($myinvocation.mycommand)"
}
} #end function

The function takes each line of code and decodes it. Each decoded line is saved as part of a long string which is eventually turned into a scriptblock and executed. To pass parameters to the secret command, which you have to know in advance, Invoke-PSCode takes a hashtable of parameters and splats them to the scriptblock.

I haven’t tested this against all possible types of scripts and code samples but I thought it was fun.

The last little function in the module is something to quickly take a string and write its reverse to the pipeline. The business part is basically a single line that gets each character from a string in reverse order and writes it to the pipeline. But using the -join operator puts it all back together.


PS S:\> $item="PowerShell is fun!"
PS S:\> -join $(for ($i=$item.length;$i -ge 0;$i--) {$item[$i]})
!nuf si llehSrewoP

The For loop starts at the length and counts down to zero. The rest of the function is merely a wrapper to this core command.


PS S:\> "I am the walrus" | ConvertTo-ReverseString
surlaw eht ma I
PS S:\> "I am the walrus" | ConvertTo-ReverseString | convertto-Reversestring
I am the walrus

I suppose if I wanted I could even combine encoding and reversal.


PS S:\> $x = "PowerShell is fun for walruses." | ConvertTo-ReverseString | ConvertTo-PSCode
PS S:\> $x
4yky{xrg} xul t{l xn ppilWvi{sT
PS S:\> $x | ConvertFrom-PSCode | ConvertTo-ReverseString
PowerShell is fun for walruses.

Although I’d better not forget the order!

So have some fun with this and maybe learn something new about PowerShell. Download PSCode.psm1. This is a module so you’ll need to put it in a folder called PSCode in your modules directory or be sure to specify the full path when you import it.

Ymtxj }nu luxmkz yt yixovz gxk juuskj yt xkvkgz ymjnw {svo