The Bourne Again PowerShell (BAPS) AWKWARD Module

Update 2013/4/3: This is an April Fools post. I think I got a bit too subtle here despite best efforts to make this kind of ridiculous. Apparently being tagged “OMGPONIES” is not enough to give it away. Yes, the script actually works, but it’s more of a roundabout way to compare the “awkward” AWK syntax with the more readable, idiomatic PowerShell. Thanks for reading. Sometimes PowerShell’s object pipeline just gets in the way. Often I find myself needing to pipe a few commands together and while I lay it out in my head, I am already unconsciously preparing the regular expressions I’ll need to convert the text to a structure required by the receiving command. Other times I am idly flicking through a well-thumbed and worn binder of common commands and their output on sheets of grid paper so I can figure out what columns I need to munge. Muscle memory is a hard beast to tame, so with that in mind, I started out designing a module that would let me scratch that regex itch, and also keep things kind of “PowerShelly.” It then dawned on me that I could probably extend this further into a full POSIX* compatibility layer, with the codename for this being WARD (Windows Adaptable Reusable Development). My first implementation for AWK/WARD looked a little like this: function awk { $input } While that implementation works well for a surprising number of cases, there are others which are a little trickier, so I figured I'd add a little intelligence to the commands for some of the more common use cases for AWK. Some examples: # count lines in a file cat text.txt | awk 'END{print NR}' # Add all fields in ALL lines and print the sum # # sheet is: # # a, b, c, d # 1, 2, 3, 4 # 1, 2, 3, 4 # 1, 2, 3, 4 cat sheet.txt | awk '{s=0; for (i=1; i<=NF; i++) s=s+$i; print s}' # Print 25 "A" characters awk 'BEGIN{while (a++<25) s=s "A"; print s}' Here's the current source of the AWKWARD BAPS module: function awk ($expression) { switch ($expression) { # count lines 'END{print NR}' { $input | measure } # print the total number of fields ("words") in all lines '{ total = total + NF }; END {print total}' { $input | measure -word } # print the sums of the fields of every line '{s=0; for (i=1; i<=NF; i++) s=s+$i; print s}' { $input | convertfrom-csv | measure -sum * | select property, sum } # add all fields in ALL lines and print the sum '{for (i=1; i<=NF; i++) s=s+$i}; END{print s}' { $input | convertfrom-csv | measure -sum * | measure -sum sum } # create a string of a specific length (e.g., generate N spaces) ([regex]'BEGIN{while \(a\+\+<(\d+)\) s=s "(.+?)"; print s}') { $matches[2] * $matches[1] } # print first N lines of file (emulates behavior of "head") ([regex]'NR < (\d+)') { $input | select -first $matches[1] } # print the last 2 lines of a file '{y=x "\n" $0; x=$0};END{print y}' { $input | select -last 2 } # print the last line of a file 'END{print}' { $input | select -last 1 } # print the last N lines of a file (circular buffer) ([regex]'{a\[NR%(\d+)\]=\$0}END{for(i=NR+1;i<=NR+(?:\d+);i++)print a\[i%(?:\d+)\]}') { $input | select -last $matches[1] } # emit matching lines for regex { $_ -as [regex] } { $input | where { ($expression -as [regex]).ismatch($_) } } default { write-warning "unsupported expression" $input } } } } If you want to play around with the module, you can autoinstall it by running the following one-liner: iex (New-Object Net.WebClient).DownloadString(“http://bit.ly/e0Mw9w”) Have fun!

About the author

Irish, PowerShell MVP, .NET/ASP.NET/SharePoint Developer, Budding Architect. Developer. Montrealer. Opinionated. Montreal, Quebec.

Month List

Page List