Kenneth M. Cruikshank
Simple Powershell script for XSLT

Kenneth M. Cruikshank
Kinect Projects

Another in a series of example Powershell scripts that may be of use.

Introduction

A common task I end up using a script for is doing something over a range of dates, or doing something periodically between some dates. If it is relatively simple interval or processing, one can of course use the task scheduler to perform the tasks at those particular time, however here I want to look at processing multiple date-time controlled items in one go.

The specific one here was first written for collecting and processing GPS data. The script would run through a collection of dates and FTp the files to the local computer, organize them (uncompressing some of them using the appropriate compression utilities).

So, I thought I would simplify the script to illustrate the basic concept.

Some features

  1. Command-line arguments
  2. Two processing options -- process now, or process later
  3. Keep track of how long script runs for

Command-Line Arguments

This is where we type something like

./myScript -ArgumentType1 Argument Value -ArgumentType2 ArgumentValue2a ArgumentValue2b

The -ArgumentType1 or -ArgumentType2 are "Keywords" that hint at what option you want. The Keyword may be followed by 0 or more "values"

The way Powershell presents the command line to the script is as an array, with each token on the command line. The array is called "$args"

  • So, args[0] -> ArgumentType1
  • args[1] -> ArgumentValue
  • args[2] -> ArgumentType2
  • etc.

There are many ways to parse this array of tokens. Because I have a simple method of Keyword-Value pairs, with the exception of two logical values. This is nice and simple. One advantage of doing this is that it is simple to move through the array, two elements at a time. The first will be the keyword, the following element is the value. That way you can use a "switch" statement and the order the keywords are used is not important. If you have more than one value to go with a keyword (option) then you will need more sophisticated.

  1. So, decide on the parameter names, and the values you will get
  2. Define ALL the variable names and give then default values
  3. Write a for- loop to step through the $args array two elements at a time

Note, although not obvious at first, the EndDate must be given after StartDate ... if it is given at all. this is to ensure that $numDays is correctly calculated. The issue is is you are only running the script for one day, all you need is the start date, however there the $numdays is set to 0, so we cannot have StartDate given after EndDate. A quirk of how the script evolved. At this point I have no reason to work around it.

Some of the options

-DataPath, StartDate and EndDate are self explanatory. Project is a name that is used in a database to identify a particular set of GPS stations, so it controls some of the Database queries.

-StepBy

Lets say I only want to process every 10th day of data

-DayOffset

This is put in especially for GPS processing. Precise ephemeris files are publised several days after a particular day. This represents a limit on processing I have to check, I cannot processing the last -DayOffset days. I can run the script without any arguments, or just this argument, and the only day run will be DayOffet before current date. This way the script can be run as an automated task every day

-DownloadOnly

-Resume

OK, things always crash! The resume is a way to pick up where you last left off, without downloading new copies of any files. I often run the script as "DownloadOnly", and when I can see that worked OK, I run the same command line bur replace DownloadOnly with resume

$myDataSet    = "Default"
$startingDate = Get-Date
$endingDate   = Get-Date
$daysOffset   = -30
$startCount   = 1
$numDays      = 1
$stepBy       = 1
$resume       = $False
$downloadOnly = $False

# Parse command-line parameters
For ($i = 0; $i -le $args.length; $i += 2) {
    switch ($args[$i])  {
        -DataPath   {
            $myDataPath = $args[$i+1]
        }
        -Project    {
            $myDataSet = $args[$i+1]
        }
        -StartDate  {
            $startingDate = Get-Date $args[$i+1]
            $daysBack = 0
            $startCount = 0
            $numDays = 0
            $daysOffset = 0
        }
        -EndDate    {
            $endingDate = Get-Date $args[$i+1]
            $daysBack = 0
            $startCount = 0
              $numDays = [System.Math]::Abs( ($endingDate - $startingDate).Days)
            $daysOffset = 0
        }
        -StepBy     {
            $stepBy = $args[$i+1]
        }
        -DayOffset {
            $daysOffset = $args[$i+1]
        }
        -Resume  {
            $resume = $args[$i+1]
        }
        -DownloadOnly    {
            $downloadOnly = $args[$i+1]
        }
    }
}

Once the parameters are set, the script should have all it needs

# Save some parameters, so we can break up processing
$savedDaysBack = $daysBack

# Download all data for this session before starting to process baselines
if ( $resume -eq $False ) {
for ($i = $startCount; $i -le $numDays; $i++) {

# Increment current day (if needed, we start by going 0 days ahead)
$currentDate = Get-Date $startingDate.AddDays($daysOffset)
$daysOffset += $stepBy

# Build datapath based on current date
$year = $currentDate.year
$doy =  "{0:000}" -f $currentDate.DayOfYear
$shortYear = Get-Date $currentDate -format "yy"
$dataPath = $myDataPath + "GPS/data/" + $year + "/" + $shortYear + "-" + $doy
$currentDate = Get-Date $currentDate -format "yyyy-MM-dd"

# Generate list of needed files. 
./Find-Stations -d $currentDate -p $myDataPath -b $myDataSet

# Unpack and organize (PS1 files)
$ftpInstructions = $myDataSet + "-ftp.txt"
./get-files $dataPath $ftpInstructions
./unpack-files $dataPath
./organize-files $dataPath
 # Generate a list of missing files (exe)
./Find-Missing -d $currentDate -p $myDataPath -b $myDataSet
 }
}

if ($downloadOnly -eq -$Falase) {
# Resore values, and start processing segment of loop
# Skip if we are only downloading files
$daysBack = $savedDaysBack
for ($i = $startCount; $i -le $numDays; $i++) {
 # Increment current day (if needed, we start by going 0 days ahead)
$currentDate = Get-Date $startingDate.AddDays($daysBack)
$daysBack += $stepBy

# Build datapath based on current date
$year = $currentDate.year
$doy =  "{0:000}" -f $currentDate.DayOfYear
$shortYear = Get-Date $currentDate -format "yy"
$dataPath = $myDataPath + "GPS/data/" + $year + "/" + $shortYear + "-" + $doy
$currentDate = Get-Date $currentDate -format "yyyy-MM-dd"
 # Generate a list of calculations (exe)
./Find-Baselines -d $currentDate -p $myDataPath -b $myDataSet
 # Process baselines (PS1)
./calculate-baselines -p $dataPath -b $myDataSet
 # Update database with results and latest log-file info (exe)
./Update-BaselineDB -d $currentDate  -p $myDataPath -b $myDataSet
#    ./Update-LogDB -d $currentDate -p $myDataPath -b $myDataSet
}
}
# Display the run-time for the script
$endAt = Get-Date
$elapsedTime = $endAt - $startAt
$myDays =  "{0:0}" -f $elapsedTime.Days
$myHours =  "{0:0}" -f $elapsedTime.Hours
$myMinutes =  "{0:0}" -f $elapsedTime.Minutes
$myOutput = "Run Time: " + $myDays + " Day(s), " + $myHours + " Hour(s), and "  + $myMinutes + " minute(s)"
Write-Output $myOutput

Geology Department
http://www.pdx.edu/geology

Copyright © 1994-2015 · K.M. Cruikshank ·
http://geomechanics.geology.pdx.edu