A Couple of PowerShell Scripts
So I went to a PowerShell training class we brought here on campus a while back. Going into a class like that already holding some general knowledge of scripting and programming, as well as specific knowledge of PowerShell (rudimentary, but still), means that by lunch-time on the first day I was bored, bored, bored. However, since I was basically getting three or four full days to play with PowerShell, I decided to play. These three little scripts are what came out of that play-time… :-)
All three of the following scripts are really very basic; nothing ground-breaking here. Hopefully as I progress in my PowerShell abilities I can provide better examples of scripts I have created to make my job easier.
The main thing to know about the scripts is that they just insert a function into the environment. To use them you must first execute the script, then call the function. In this way you can have the scripts execute in your Microsoft.PowerShell_profile.ps1 file, and then the functions will be available in the environment without having to execute the actual script. (EDIT: You’ll note that even I don’t use these scripts anymore; I never updated them and the GNU versions work just fine. I’m leaving this post here only for historical purposes.)
ps_head.ps1
The first script is called ps_head.ps1
and in all reality is just a
tiny little function. It implements the most basic function of the Unix
head
command: it returns the first few lines of a file. The syntax
is:
ps_head [-lines] <file>
Here’s the script:
if (Test-Path function:ps_head) { Remove-Item function:ps_head }
# head file -lines
function global:ps_head($path,$lines="-10") {
if ($lines.GetType().Name -eq "String" ) {
$lines = $lines.trim("-")
}
if ($path -ne $Null) {
if (Test-Path $path) {
Get-Content -path $path -totalCount $lines
}
}
}
Write-Host "Added ps_head to global functions." -Fore White
ps_tail.ps1
Next in line is ps_tail.ps1
which, as you can probably guess, does the
same thing as the Unix tail
command. Syntax is the same as above:
ps_tail [-lines] <file>
if (Test-Path function:ps_tail) { Remove-Item function:ps_tail }
# ps_tail file -lines
function global:ps_tail {
param ($path, $lines="-10", [switch] $f)
if ($lines.GetType().Name -eq "String" ) {
$lines = [string]$lines.trim("-")
}
if ($path -ne $Null) {
if (Test-Path $path) {
$content = Get-Content -path $path
for($i=$content.length - $lines; $i -le $content.length; $i++) {
$content[$i]
}
}
}
}
Write-Host "Added ps_tail to global functions." -Fore White
ps_grep.ps1
The third and final script for this post is ps_grep.ps1
. It
implements a couple of grep
’s command-line options; namely, -c
and
-i
. The syntax is:
ps_grep [-c] [-i] pattern <file>
(Each command-line switch needs to be provided separately; currently
you’re not able to run them all together, as in “grep -cRi pattern *.*
”. I might work on that. [EDIT: clearly, I didn’t, and
won’t.])
Something to note is that ps_grep
will loop through all of an object’s
properties in search of the supplied regex pattern and not just the
Name
property, etc. Also, if you use the function in a pipeline and
pattern is a simple string (i.e., not a regular expression), the match
will always be case-insensitive. When I have a bit of time I’ll try to
figure that one out.
if (Test-Path function:ps_grep) { Remove-Item function:ps_grep }
function global:ps_grep([switch] $c, [switch] $i, $pattern, $file="", $inputObject=$Null) {
BEGIN {
# This section executes before the pipeline.
# $count will contain the number of matches found.
$count = 0
if ($inputObject) {
$inputObject
}
} # end 'begin'
PROCESS {
# This section executes for each object in the pipeline.
# Did we get a filename? If so we're operating on a file, not stdout.
if ($file -ne "") {
# Is the filename a valid file?
if (Test-Path $file) {
# Grep the file for $pattern, if we were given $pattern.
if($pattern) {
if (!$i) {
# User has specified as case-sensitive match.
$case = "-CaseSensitive"
}
$command = "Select-String $case -pattern $pattern -InputObject (Get-Item $file)"
foreach ($match in Invoke-Expression($command)) {
if (!$c) {
# only output lines if -c was not specified.
Write-Host $match
}
# Increment our count.
$count++
}
} # end 'if ($pattern)'
} # end 'if (test-path)'
else {
# The filename passed does not exist. Die.
Write-Host "File does not exist."
} # end 'else'
} # end 'if ($file...)'
# Didn't get a filename, so we're operating on stdout.
elseif($pattern) {
# Save the pipelined object into an internal var to avoid confusion below.
$obj = $_
# Loop through $obj's properties and look for $pattern
foreach ($prop in ($obj | Get-Member -MemberType Properties) ) {
if($obj.($prop.name.ToString()) -match $pattern) {
if (!$c) {
# Found a match, output the result to stdout.
Write-Output $obj | ($MyInvocation.InvocationName) -inputObject $_
}
# Increment our count.
$count++
# No reason to keep searching this property.
break
} # end 'if ($obj...)'
} # end 'foreach'
} # end 'elseif'
} # end 'process'
END {
# If '-c' was specified on the command line, output the total count.
if ($c) {
Write-Host $count
} # end 'if ($c)'
} # end 'end{}'
} # end function
Write-Host "Added ps_grep to global functions." -Fore White
If you have any ideas, let me know how I can improve these little functions.