PowerShell and Unix crontables

Recently, I working on ways of documenting crontables we had on the various Unix servers we had a responsibility for. In the end I opted for a PowerShell solution to create an XML file for each of the crontables.

The program looks as follows:

# Creates an XML file from the output of the Unix command
# $ crontab -l > cron.txt.
#
# This program is also an example of creating XML files from PowerShell.
#

function Write-Dataline($xwriter, $cronbits, $id) {

  $xwriter.WriteStartElement("Line");
  $xwriter.WriteAttributeString("ID", "$id");

  $xwriter.WriteStartElement("CronTime");
  $xwriter.WriteString($cronbits.DateTime);
  $xwriter.WriteEndElement();

  $xwriter.WriteStartElement("Executable");
  $xwriter.WriteString($cronbits.Executable);
  $xwriter.WriteEndElement();

  $xwriter.WriteStartElement("Logfile");
  $xwriter.WriteString($cronbits.Logfile);
  $xwriter.WriteEndElement();

  $xwriter.WriteStartElement("ErrorRedirect");
  $xwriter.WriteRaw($cronbits.ErrorRedirect);
  $xwriter.WriteEndElement();
write-output ("error thing = $($cronbits.ErrorRedirect)");
  $xwriter.WriteFullEndElement();     # <-- Close element ''Line''.

} #end function Write-Dataline

function Close-XmlFile($xwriter) {

  Write-Host "Closing the xml file";
  # The closing bits of the XML file
  $xwriter.WriteEndElement();     # <-- Close root element
  $xwriter.WriteEndDocument();    # <-- Close the document

  # Finish The Document
  $xwriter.Finalize;
  $xwriter.Flush;
  $xwriter.Close();

} #end function Close-XmlFile

function Get-Lineparts([String]$str) {
  begin {
    $props = [ordered]@{
       DateTime = "";
       Executable = "";
       Logfile = "";
       ErrorRedirect = "";
    }
    $cronbits = New-Object psobject -Property $props;

    $props = [ordered]@{
       Start = 0;
       End = 0;
    }
    $pos = New-Object psobject -Property $props;
  } #end begin


  process {

    # Get the date/time part of the cron line.
    $pos.End=$str.IndexOf('/');
    $cronbits.DateTime=$str.Substring($pos.Start,$pos.End).Trim();

    # Get the executable, i.e. the shell script bit.
    $pos.Start=$pos.End;
    $pos.End=$str.IndexOf('>', $pos.End);
    $cronbits.Executable=$str.Substring($pos.Start,($pos.End-$pos.Start)).Trim();

    # Get the logfile name.
    $pos.Start=$str.IndexOf('/', $pos.End);
    $pos.End=$str.IndexOf('2>&1', $pos.End);
    $cronbits.Logfile=$str.Substring($pos.Start,($pos.End-$pos.Start)).Trim();

    # Get the error redirect.
    $cronbits.ErrorRedirect=$str.Substring($pos.End).Trim();

  } #end process


  end {
    return $cronbits;
  } #end end

} #end function Get-Lineparts

function Set-XmlWriterSettings {

  $xsettings = New-Object -TypeName System.Xml.XmlWriterSettings;
  $xsettings.CloseOutput = $true;
  $xsettings.Encoding = [System.Text.Encoding]::UTF8;
  $xsettings.Indent = $true;
  $xsettings.IndentChars = "    "; # 4 spaces
  $xsettings.WriteEndDocumentOnClose = $true;
  $xsettings.CheckCharacters = $true;
  #$xsettings.NewLineChars = "0xoa";
  $xsettings.NewLineOnAttributes = $true;
  $xsettings.OmitXmlDeclaration = $false;
  $xsettings.ConformanceLevel = [System.Xml.ConformanceLevel]::Auto;

  return $xsettings;

} #end function Set-XmlWriterSettings

function Set-XmlFile {

  # Do some initialising
  $filePath = "C:\ian\PowerShell\good03.xml";
  $xset = Set-XmlWriterSettings;
  [System.Xml.XmlWriter]$xwriter = [System.Xml.XmlWriter]::Create($filePath,$xset);
  $server="MyServer";
  $owner="Ian";
  $ymd=(Get-Date).ToString("s");

  # Write the header elements to our XML file
  $xwriter.WriteStartDocument();               # <-- Start the document
  $xwriter.WriteStartElement("CronTable");     # <-- Important root element

  $xwriter.WriteStartElement("Server");
  $xwriter.WriteString($server);
  $xwriter.WriteEndElement();

  $xwriter.WriteStartElement("CronOwner");
  $xwriter.WriteString($owner);
  $xwriter.WriteEndElement();

  $xwriter.WriteStartElement("DateStamp");
  $xwriter.WriteString($ymd);
  $xwriter.WriteEndElement();

  $xwriter.WriteStartElement("CronLines");

  return $xwriter;

} #end function Set-XmlFile


# ------------------------
# Main routine starts here
# ------------------------

$counter=0;
$infile="C:\ian\PowerShell\single_line.txt";
$input = New-Object -TypeName System.IO.StreamReader -ArgumentList $infile;
$ymd=(Get-Date).ToString("s");
$cronbits=$null;

#Create and write the header to the XmlFile.
$xwriter=Set-XmlFile;

$inrec = $input.ReadLine();
while ($inrec -ne $null) {
   Write-Output ($inrec);
   $counter++;
   $cronbits=Get-Lineparts $inrec;
   Write-Dataline $xwriter $cronbits $counter;
   $cronbits | Format-list;

   $inrec = $input.ReadLine();
}

$input.Close();
$input.Dispose();

Close-XmlFile($xwriter);

Write-Host "`nRecords read: $counter";
Write-Host "The date is $ymd";

Input and output files are hard coded so the program could be more flexible.

Input file – is at line 148.

Output file – is at line 113.

The input file is created on the Unix server with the command:

$ crontab -l > inputfile.txt

The man page will cover this command. The input file I used was:

15,30,45,0 * * * * /u01/Acer/MorrisGarages/exec/GreenBank.sh > /u01/Acer/MorrisGarages/GreenBank.log 2>&1
45 19 * * * /u04/Acer/Minolta/exec/Hartung–Boothroyd.sh > /u04/Acer/Minolta/logs/Hartung–Boothroyd.log 2>&1
25 04 * * * /u04/Boeing/Digital/Jodrell-Bank.sh > /u04/Boeing/Digital/Jodrell-Bank.log 2>&1

The output XML file will look like this:

<?xml version="1.0" encoding="utf-8"?>
<CronTable>
    <Server>MyServer</Server>
    <CronOwner>Ian</CronOwner>
    <DateStamp>2015-04-05T18:49:17</DateStamp>
    <CronLines>
        <Line
            ID="1">
            <CronTime>15,30,45,0 * * * *</CronTime>
            <Executable>/u01/Acer/MorrisGarages/exec/GreenBank.sh</Executable>
            <Logfile>/u01/Acer/MorrisGarages/GreenBank.log</Logfile>
            <ErrorRedirect>2>&1</ErrorRedirect>
        </Line>
        <Line
            ID="2">
            <CronTime>45 19 * * *</CronTime>
            <Executable>/u04/Acer/Minolta/exec/Hartung–Boothroyd.sh</Executable>
            <Logfile>/u04/Acer/Minolta/logs/Hartung–Boothroyd.log</Logfile>
            <ErrorRedirect>2>&1</ErrorRedirect>
        </Line>
        <Line
            ID="3">
            <CronTime>25 04 * * *</CronTime>
            <Executable>/u04/Boeing/Digital/Jodrell-Bank.sh</Executable>
            <Logfile>/u04/Boeing/Digital/Jodrell-Bank.log</Logfile>
            <ErrorRedirect>2>&1</ErrorRedirect>
        </Line>
    </CronLines>
</CronTable>

The XML tag CronLines contain details of each of the lines in my example input file. The ID attribute indicating which line in particular. So the tag <Line ID=”3″> gives the detail for line 3 of my input file, Jodrell-Bank.sh.

Lines 156-165 is the main processing loop in which each line is read in from the input file.

Line 160 invokes function Get-Lineparts passing to it, an input record.

Funtion Set-XmlWriterSettings specifies a set of features to support on the XmlWriter object. See XmlWriterSettings Class for further details. This is really initialising my XML object. How many character spaces to indent and so on.

Function Write-Dataline writes data to the tags CronTime, Executable, Logfile and ErrorRedirect.

Function Get-Lineparts tokenizes the input string record into its constituent parts.

Advertisements
This entry was posted in powershell and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s