Today I thought I'd share my PowerShell solution to a recent Iron Scripter challenge. The challenge was to create PowerShell code that would create nonsense documents, with a goal of creating 10 sample files filled with gibberish. Yes, other than maybe wanting some test files to work with, on its face the challenge appears pointless. However, as with all of these challenges, or even the ones in The PowerShell Practice Primer, the journey is the reward. The true value is learning how to use PowerShell, and maybe discovering a new technique or command. The hope is that during the course of working on the challenge, you'll improve your PowerShell scripting skills. And who knows, maybe even have a little fun along the way.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Nonsense Documents
I've put everything in a module on Github called PSNonsense.
I approached this in a building block approach. I begin with a function to create a nonsense word. This is simple enough. All I need to do is select a number of random characters from a pool and join them together.
$letters = [Char[]]'abcdefghijklmnopqrstuvwxyz'
#add some diacritical characters
$letters += [Char]0x00EB,[Char]0x00E4,[Char]0x00E9
-join ( $letters | Get-Random -count $Length)
The function, as do all of the functions, includes parameters to define the length or count, using a random default. I also use a ValidateSet attribute to verify the parameter value a user might enter.
Param(
[Parameter(Position = 0, HelpMessage = "Indicate the word length between 1 and 10.")]
[ValidateRange(1, 10)]
[int]$Length = (Get-Random -Minimum 1 -Maximum 10)
)
Creating a nonsense word is this easy.
PS C:\> New-NonsenseWord -Length 7
ëfswäna
A nonsense sentence is just a collection of nonsense words. But there was an additional challenge to insert punctuation and to make sure sentences ended in a period. I added code to randomly end some sentences in a question mark or exclamation point.
$punct = ", ", "; ", " - ", ": "
#define a flag to indicate if random punctuation has been inserted.
$NoPunct = $true
1..($WordCount - 1) | ForEach-Object -Begin {
#make sure we start the sentence with a word
[string]$sentence = New-NonsenseWord
} -process {
#insert random punctuation into the sentence, but only once
if (($WordCount -ge 10) -AND (Test) -AND $NoPunct) {
$sentence += $punct | Get-Random -Count 1
$NoPunct = $False
}
$sentence += "{0} " -f (New-NonsenseWord)
}
#capitalize the first word of the sentence.
#does the sentence end in a period, exclamation or question mark.
#The period should be the default most of the time
$rn = Get-Random -Maximum 100 -Minimum 1
Switch ($rn) {
{$_ -ge 90} {$end = "?" ; break}
{$_ -ge 82} { $end = "!"}
Default { $end = "."}
}
$out = "{0}{1}{2}" -f ([string]$sentence[0]).ToUpper(), $sentence.substring(1).TrimEnd(),$end
The function includes a private function called Test which randomly determines if I should do something, like insert punctuation.
PS C:\> New-NonsenseSentence -WordCount 11
Urip; élthe qkvipwen kym h fäuq cwuoéhjnä ikafysqol eom ikäb yke.
Creating a paragraph means joining a specified number of sentences together.
$raw = 1..$SentenceCount | Foreach-object {New-NonsenseSentence}
($raw -join " ").trimend()
Since samples are getting a bit longer, here's a screen shot.
Notice the puncutation.
Creating a document is nothing more than a collection of paragraphs.
#insert a return after each paragraph
1..$ParagraphCount | ForEach-Object {New-NonsenseParagraph;"`r"}
With this function, New-NonsenseDocument, I can use code like this to create 10 documents.
1..10 | ForEach-Object {
$filename = [System.IO.Path]::GetRandomFileName()
#replace the extension
$filename = $filename -replace "\.\w+", ".txt"
#build the path
$path = Join-Path -path $env:TEMP -ChildPath $filename
#create the document
#encode as UTF8 to save the diacritical characters
New-NonsenseDocument -ParagraphCount (Get-Random -Minimum 3 -Maximum 10) |
Out-File -FilePath $path -Encoding utf8
#view the result
Get-Item $path
}
I'm useing the .NET Framework to generate a randome file name and then using a regex pattern to replace the extension with .txt extension. The final file path is built using Join-Path which is recommended instead of trying to concatenate strings together. You can view the files in the Samples folder in the PSNonsense repository.
Markdown Nonsense
The challenge had an extra-credit requirement to create a nonsense markdown document. A markdown document has specific requirements for headings. Ultimately, I built the file by using a here-string, and inserting randomly generated markdown elements.
To simplify things (honestly), I wrapped the PSNonsense commands into a few helper, or cheater, functions.
function New-Heading {
(New-NonsenseSentence -WordCount (Get-Random -Minimum 1 -Maximum 5)) -replace "[.!?]", ""
}
function New-CodeFence {
$cmd = "$((New-NonsenseSentence -WordCount (Get-Random -Minimum 2 -Maximum 6)) -replace "[.!?]",'')"
#the backtick needs to be escaped so that end result is a proper markdown code fence.
#there should be 6 backticks.
$cf = @"
$('`'*6)powershell
PS C:\> $cmd
$('`'*6)
"@
$cf
}
function New-SubHead {
Param(
[int]$Level = 2
)
$h = "#"*$level
$head = "{0} {1}" -f $h, (New-Heading)
#write-host $head -ForegroundColor red
$out = @"
$head
"@
1..(Get-Random -Maximum 4) | ForEach-Object {
$p = (New-NonsenseParagraph -SentenceCount (Get-Random -Minimum 4 -Maximum 10) -outvariable pv)
if ((Get-Random)%3) {
#randomly format a string
$wds = ($p.split()).where( {$_ -match "\w+"})
$hl = $wds | Select-Object -skip (Get-Random -Minimum 7 -Maximum ($wds.count - 20)) -First (Get-Random -Minimum 1 -Maximum 7)
#randomly decide how to format
Switch ((Get-Random)) {
{$_%3} {$f = "*$($hl -join " ")*" }
{$_%5} {$f = "__$($hl -join " ")__"}
{$_%7} {$f = "__*$($hl -join " ")*__" }
{$_%4} {$f = "``$($hl -join " ")``" }
{$_%2} {$f = "[$($hl -join " ")](https://www.$(New-NonsenseWord).com)"}
}
#left justify to send to the here string to avoid extra spaces
$out += ($p -replace ($hl -join " "), $f)
} #if %3
else {
$out += $p
}
$out += "`n`n"
<#
the out variable is itself an array. I want to get the first item in that array,
which will be a string and then the first character in that string.
#>
if ($pv[0][0] -match "[aeifuylm]") {
#$out+="`n"
#increment if the next level is 4 or less
if ($level + 1 -le 4) {
$out += New-SubHead -level ($level + 1)
}
else {
#repeat the level
$out += New-SubHead -level $level
}
}
elseif ($pv[0][0] -match "[pjqrst]" ) {
$out += New-CodeFence
$out += "`n`n"
#add a bit more verbiage
$out += New-NonsenseParagraph -SentenceCount (Get-Random -Minimum 1 -Maximum 4)
}
} #foreach object
$out
} #New-SubHead
I gave myself even more of a challenge by randomly inserting code fence sections, and formatting random phrases. I think the comments in the functions explain my thought process. With these functions, and my PSNonsense module, I can run code like this to create a nonsense markdown document.
$md = @"
# $(New-Heading)
$((New-SubHead).trimend())
__My additional New-Headings follow.__
"@
1..(Get-Random -maximum 5) | ForEach-Object {
$md += New-SubHead
}
$md += "Updated *$(Get-Date)*."
$md
#encode as UTF8 to save the diacritical characters
$md | Out-File -FilePath c:\work\nonsense.md -Encoding utf8
The here-strings include the necessary blank lines to create a proper markdown document. Note the message about file encoding. If you use special characters and don't specify UTF8, the file might have '?' characters in place. One thing I noticed in VS Code is that in editor mode the markdown document is fine. But when I use the preview mode, it doesn't detect the special characters. I don't iknow if it is a bug or a setting I haven't found yet. Regardless, you can find my nonsense markdown and a PDF version in the Samples folder on Github.
I hope you'll take some time to look at code in the module's repository. Ideally, you'll try it out yourself to see how it works and why. If you learn something new, I hope you'll let me know.
In the mean time, keep using PowerShell every day. And if you haven't, checkout the other scripting challenges at https://ironscripter.us and try your hand at solving them. There's no time limit or expiration date and you might be surprised what you learn.
1 thought on “Friday Fun – A PowerShell Nonsense Challenge”
Comments are closed.