TweetFollow Us on Twitter

Printer Usage Reports with AppleScript and CUPS

Volume Number: 19 (2003)
Issue Number: 11
Column Tag: Programming

Patch Panel

Printer Usage Reports with AppleScript and CUPS

by John C. Welch

Finding out who's printing what in Mac OS X

Print Logs

One of the biggest issues for any network administrator is dealing with print accounting. Now, with Mac OS X server, you have the tools to deal with printer accounting, and Mac OS X Server 10.3 should give you even more tools for managing printers, and printing. But what about folks who are just sharing a printer from Mac OS X? One could argue that in this case, you don't need to manage your printer access at the same level that you do in a server - based network. However, for many small companies, or small networks that share printers, it's good to be able to know who is printing what and how much paper they are using. It's also a neat exercise for AppleScripters, and sometimes, just doing something in a way that most people wouldn't think to, and having it work, is pretty cool in and of itself.

Again, if you are looking for brilliance in AppleScript, talk to people like Sal Soghoian, or Bill Briggs, or Paul Berkowitz. I, like any other programmer or scripter, do things the way they make sense to me. If you can, as has happened in the past, rework my code and get huge efficiency gains, etc. out of it, by all means do so, and send it back to me. I can use the help! I also realize that there are a half dozen shell utilities that I could use to make this a LOT faster, and simpler. But then, I wanted to do this entirely in AppleScript, and so, have to live with its rather anemic text processing abilities. (Yes, yes, Perl folks can feel rightly smug. But I can script Photoshop, so nyah!)

So the object of this script is to process the printer logs that are created by CUPS when you print in Mac OS X, and output a number of things as a tab-delimited text file, giving you the option to then open it in Microsoft Excel or Filemaker Pro, or just leave it where it is. The text file lists the total number of printers, total number of users printing to those printers, total number of pages printed through that machine, total number of pages per printer, and total number of pages per user. Since this script uses CUPS, it obviously requires at least Mac OS X 10.2 or later.

The CUPS log

Since we're talking about the CUPS log, we should take a quick look at it. Every time you print in Mac OS X, the CUPS printing system records the printer name, the user name, the page count for each page in a job, the date/time stamp for each page, the page number, the total number of pages, optional billing information, and the name of the host that sent the job to the host acting as a server to the printer. The printer in this case doesn't have to be a physical printer. If you have Adobe Distiller 6 installed, it also logs jobs sent to the "Adobe PDF" printer, even though it only creates PDF files.

In a Mac OS X system, the log file we are using is the page_log file, stored in /var/log/cups/. While you can't easily modify that file without using sudo, or su, anyone can read the file, so this script can be run by any valid user on an OS X system. A sample of the log file from my machine is shown below.

Printer name                 user   job id   Date/Time stamp          page/copy #'s    acct.   host
HP_LaserJet_8150_Series2    jwelch    16    [08/Oct/2003:10:34:58 -0500]    1 1    -    localhost
HP_LaserJet_8150_Series2    jwelch    16    [08/Oct/2003:10:35:00 -0500]    2 1    -    localhost
HP_LaserJet_8150_Series2    jwelch    16    [08/Oct/2003:10:35:01 -0500]    3 1    -    localhost
HP_LaserJet_8150_Series2    jwelch    16    [08/Oct/2003:10:35:02 -0500]    4 1    -    localhost
HP_LaserJet_8150_Series2    jwelch    16    [08/Oct/2003:10:35:04 -0500]    5 1    -    localhost
HP_LaserJet_8150_Series2    jwelch    16    [08/Oct/2003:10:35:06 -0500]    6 1    -    localhost

This is from a 6 page job, printed to an HP LaserJet 8150, by me. The printer is on an AppleTalk connection to my machine. So going field by field, we see the printer name is "HP_LaserJet_8150_Series2, the user sending the print job is "jwelch", the job id is "16", the Date/Time stamp tells us this job was printed on October 8, 2003, around 10 am, there were 6 pages for one copy, (note that if the printer doesn't have a "mopy" function, or multiple copies per job, this is always 1), the accounting field is a "-" since by default, Mac OS X doesn't include this information, and since this job was sent to my computer by my computer, the host name is "localhost". In the page_log file, the fields are space delimited, but I used tabs here to break the fields up better. As well, there is one entry per page printed. Both of the last two bits of information will be important to us as we process this file in the script.

Script Properties

property thePrinterNameList : {} --a list of printer names, unsorted, one per line in 
   the thePageLogContentsList
property thePrinterTypeList : {} --a list of unique printer names, 1 line per printer name, 
   derived from the getThingTypes handler
property wasThereAMatch : 0 --used in the getThingTypes handler as a match flag
property theUserNameList : {} -- a list of user names, unsorted, one per line in the 
property theListedUsers : {} --a list of unique user names, 1 line per user name, 
   derived from the getThingTypes handler
property totalPagesForMachine : 0 --holds the total pages printed by the machine
property pagesByUserList : {} --a list with one entry per user page total. THIS ONLY HAS THE NUMBER, 
property pagesByPrinterList : {} --a list with one entry per printer page total. 
--YOU HAVE TO MATCH IT WITH thePrinterTypeList TO GET PAGES/printer!
property theTotalCountedThings : 0 --used as a counter in the getTheList handler
property theTotalCountedThingsList : {} -- generic handler processed list variable for 
   passing lists back and forth
property theListedThing : {} --generic list variable for passing data to handlers
property theCurrentThing : "" --generic text variable for various handlers, used mostly for 
   matching ops
property theCurrentThingPages : "" --used as a variable in getDataByPrinterOrUser for holding 
   page counts by user or printer
property theCurrentLine : "" --when processing thePageLogContentsList, holds one line of that 
   list as a string
property theCurrentLineContentsList : {} --when processing thePageLogContentsList, holds one 
   line of that list as a list
property usernameOrPrinterName : 0 --handler variable, tells if you are dealing with a printer 
   name or a user name, is a 
--list placeholder, 2 = user, 1 = printer
property userOrPrinter : "" --text flag used in getDataByPrinterOrUser for determining user or 
   printer in report line
property theLastThing : {} -- generic handler unprocessed list variable for passing lists back 
   and forth
property theListedThingType : "" -- used as an inner repeat loop matching variable holder in 
   the getThingTypes handler
property theListedThingPages : {} -- generic handler list variable for processing 
   pagesByPrinterList and the pagesByUserList
property thePrinterReportFilePath : "" -- alias to where the tab delimited file will be, 
   set by user
property thePageLogAlias : "" --alias to the cups page log file
property thePageLogPath : "" --POSIX path to the cups page log file
property thePageLogFile : 0 --file handle when reading the cups page log file
property thePageLogContents : "" --holds the contents of the cups page log file
property thePageLogContentsList : {} --holds the list version of thePageLogContents
property theWrite : "" --placeholder used to generate the report file

The comments following each - are the ones I used while developing the script. I have learned over the years that a certain reflexive obsession with regard to commenting comes in handy when revisiting code for things like updates, or writing articles on it. Because AppleScript's native text handling capabilities are anemic at best, we use a lot of lists to compensate. While there are a number of AppleScript extensions, or OSAX that make up for this, I try to avoid them where possible, so my scripts will run on the largest number of machines possible without requiring the user to download and install extensions. If the property names and comments don't make sense at the moment, don't worry. As we go through the rest of the code, you'll see what they're all used for.

The Main Script Body

The first thing we see is a call to System Events, a part of Mac OS X used for certain system functions that were handled by the Finder in Mac OS X. While we could use the Finder for this, System Events is the more correct way to handle our needs here. In this case, we ask System events for the name of every disk labeled a startup disk, and get the name of the first one. Since there should only be one of these, assuming the first one is the one we want poses little risk of error. Since a list is returned, we have to grab the first item from the list, even if it is the only item:

tell application "System Events" --get the name of the startup disk code
   set theStartupDiskList to (the name of every disk whose startup is true) --gets a list 
      of all startup disks
   set theStartupDiskName to item 1 of theStartupDiskList --on any running Mac, there is 
      only one startup disk, so it's always item 1
end tell

The next set of lines get an alias to the page_log file, and the POSIX, or Unix - style path version of that alias. The first line also concatenates the name of the startup disk that we got earlier so that we have a full alias to the page_log file. The conversion to the POSIX path handles renaming the disk name in the alias to a more proper "/":

set thePageLogAlias to (theStartupDiskName & ":private:var:log:cups:page_log") 
   as alias --get the path to the page log file. Hardcoded unless it needs to move about
set thePageLogPath to POSIX path of thePageLogAlias --posix path of thePageLogAlias, just in case

The next lines open the page_log file without write permission. It would be hard to open it with write permission unless root was running the script, and this way, we avoid accidents. We then dump the contents of the file to the variable "thePageLogContents". We then close the file access handler so that we don't have open file handlers laying about:

set thePageLogFile to open for access thePageLogAlias without 
   write permission --open the page log file read-only, it's all we need, and it's safer
set thePageLogContents to read thePageLogFile --dump the contents of the file into a variable
close access thePageLogFile --close the file handler, leaving those open is bad

Now, so that we can more easily use the contents we've just read, we turn it into a list, with each line in the log being one item in the list. To do this, we use the end of line characters as text item delimiters, by telling AppleScript to use the "\n" character as its new text item delimiter, saving the original value to the variable "oldDelims" so we can restore it later. Then we make a list from every text item in thePageLogContents, one item per line. Once that's done, we clean up after ourselves by resetting AppleScript's text item delimiters to their original value:

set oldDelims to AppleScript's text item delimiters --temp store Applescript 
   current delimiters
set AppleScript's text item delimiters to "" --use \n as the current delimiter, since it's a 
   Unix text file
set thePageLogContentsList to (every text item of thePageLogContents) --turn the page log 
   into a list
set AppleScript's text item delimiters to oldDelims --reset the delimiters

We then take advantage of CUPS creating one line per page printed, and use the length of the list to get us that information, and store it in totalPagesForMachine. Since the last line of the log is blank, we subtract one from the length to get the correct number of pages:

set totalPagesForMachine to ((length of thePageLogContentsList) - 1) 
   --the last line is always blank

The next bit of code takes advantage of the consistency of the page_log format. Since it always has the same number of fields and each field always has the same contents, we can make some assumptions that let us get the printer name and user name. In this case, the first field is always the name of the printer, and the second field is always the user name. So, what we do is make two lists, one of user names and one of printer names by manipulating the AppleScript text item delimiters for each entry in thePageLogContentsList, which is, as we should still recall, one line from the original page_log file. By using the space character for the delimiter, we temporarily turn each entry in thePageLogContentsList to a list of its own, then grab item one and shove it into a list of printer names, then shove item two into a list of user names. Yes, we'll have duplicates, but we'll handle those in a bit. Once we've done this for the entire thePageLogContentsList, we're pretty much done with the main body of code for the script for the moment, as the next few sections are mostly calls to handlers:

repeat with x from 1 to (length of thePageLogContentsList) --this is to get us 
   two lists...user names and printers
   set theCurrentLine to item x of thePageLogContentsList --grab a line out of the list
   set oldDelims to AppleScript's text item delimiters --temp store Applescript current delimiters
   set AppleScript's text item delimiters to " " --use space as the current delimiter
   set theCurrentLineContentsList to (every text item of theCurrentLine) --turn the page log  
      line into a list
   if item 1 of theCurrentLineContentsList is not "" then --check for that blank line
      set the end of thePrinterNameList to item 1 of theCurrentLineContentsList --item 1 is 
         always the printer
      set the end of theUserNameList to item 2 of theCurrentLineContentsList --item 2 is always 
         the the username
   end if
   set AppleScript's text item delimiters to oldDelims --reset the delimiters
end repeat

So, now we have a list of users and a list of printers, but at the moment, they aren't that useful. Since each job has one entry per page, we are going to have a lot of repeats, so we need to make sure we only have one of each user or printer. First, lets take care of the printers. While we could use unique handlers for each of these, we don't have to. The code to handle the printers and the names is almost identical, so with a little work, we can use one handler to process both. One thing I did here, which you may or may not agree with procedurally, is to use generic names for the handlers. If nothing else, it makes it harder for me to forget that I'm out of the main body of code, and in a handler.

Calling our handler, and getting a return back takes three lines. The first sets our handler variable to the contents of thePrinterNameList. The second sets the variable thePrinterTypeList to the results of getThingTyeps(theListedThing). Once the handler is finished, then we clear theTotalCountedThingsList, (used in the handler):

set theListedThing to thePrinterNameList --I prefer using generic vars for 
   multipurpose handlers, it's easer for me
set thePrinterTypeList to getThingTypes(theListedThing) of me --get a list of unique printers
set theTotalCountedThingsList to {} --clear this list

The getThingTypes(theListedThing) Handler

Now, let's take a look at the getThingTypes(theListedThing) handler. The handler uses a repeat loop to iterate through the list, theListedThing. We check to see if theLastThing is blank, which indicates that this is the first time through the list. If it is, we set theLastThing to the first item in theListedThing. This is important, since this handler's purpose is to create a list of unique user or printer names. We also set the current item of theListedThing to the end of theTotalCountedThingsList, since this is the first time through the list:

repeat with y from 1 to (length of theListedThing) --run the list of names
      if theLastThing is "" then --if this is the first iteration   
         set theLastThing to item y of theListedThing --set theLastThing as a comparator
         set the end of theTotalCountedThingsList to item y of theListedThing -- first name 
            in the list is the first name on the new list
      end if

Next, we set theCurrentThing to the current item of theListedThing. This is going to be the prime variable used to check for duplicates:

set theCurrentThing to item y of theListedThing --test against the current item

We now compare theCurrentThing to theLastThing. If they aren't equal, good. But since we have to check for more than one duplicate, we can't stop there. This is where theTotalCountedThingsList comes into play. We run another repeat loop through that list, assigning theListedThing type to the current item of the list as we run through this loop, and compare it to theCurrentThing. If there is a match, we set wasThereAMatch to 1, and exit the loop. If we make it through theTotalCountedThingsList without a match, we set wasThereAMatch to 0. Once we are out of the loop, if wasThereAMatch is 0, then there was no match, and theCurrentThing is a unique name, so we tack it onto the end of theTotalCountedThingsList. We then set wasThereAMatch to 0, to clear the loop. This is done for every entry in theListed thing. Once we have checked every entry in that list, we clear wasThereAMatch and theLastThing. We pass theTotalCountedThingsList, which is now a list of unique printers or usernames back to the calling code line, and that's the end of the handler.

if theCurrentThing <> theLastThing then --no, not the same thing
         repeat with z from 1 to (length of the theTotalCountedThingsList) --run the list of 
            printer types we already have
            set theListedThingType to item z of theTotalCountedThingsList --grab a name to test 
            if theCurrentThing = theListedThingType then --is this printer already in here?
               set wasThereAMatch to 1 --yes
            end if
         end repeat
         if wasThereAMatch = 0 then --new printer type
            set the end of theTotalCountedThingsList to theCurrentThing --stick this in the list 
               of printer types
         end if
         set wasThereAMatch to 0 --clear the var in the loop
      end if
   end repeat
   set wasThereAMatch to 0 --clear the var
   set theLastThing to "" --clear the var
   return theTotalCountedThingsList --here's a list of unique names

Our next three lines back in the main body of code call getThingTypes(theListedThing) to get a list of unique user names in theListedUsers, and works the same as what we just saw for getting printer names. We, therefore, have the same handler used to get two different lists of data, without duplicating the code. Code reuse is real, and it rocks.

So, we now have a list of users and a list of printers, and the total pages printed. Not bad, but we need to know how many pages each user printed, and how many pages each printer printed. Again, we're going to use the same handler to get this information, but we also need to track what we're trying to get, printer or user info, so we use the variable usernameOrPrinterName to do this. Remember that in the page_log file, the printer name is always the first field for each entry and the user name is always the second field. If we are looking for user data, we set usernameOrPrinterName to 2, and if we want printer data, we set usernameOrPrinterName to 1.

We again use theListedThing as the list variable we pass, and since we are looking for user data, we set it to the contents of theListedUsers. We set usernameOrPrinterName to 2, indicating we want user data, and then pass both of them, along with thePageLogContentsList to getTheList, and return the results to pagesByUserList. Once we are done with the handler, we again clear theTotalCountedThingsList:

set theListedThing to theListedUsers
set usernameOrPrinterName to 2 --we're going to get user pages
set pagesByUserList to getTheList(theListedThing, thePageLogContentsList, usernameOrPrinterName) 
   of me
set theTotalCountedThingsList to {}

The getTheList(theListedThing,thePageLogContentsList, usernameOrPrinterName) Handler

Now, lets look at the getTheList(theListedThing,thePageLogContentsList, usernameOrPrinterName) handler. We use a repeat loop to iterate through theListedThing, which, in this case is the list of unique user names. We set theCurrentThing to the current item in that list. We then set another repeat loop for thePageLogContentsList. We set theCurrentLine to the current item of that list. Next, we call on our old friend, AppleScript's text item delimiters to turn theCurrentLine's contents into a list, theCurrentLineContentsList. Since CUPS loves to insert blanks, we make sure that item 1 isnt' blank. Now, the next part is a little tricky to follow. We have set theCurrentThing to the current item of theListedThing, which is for now, a list of user names. We had also set usernameOrPrinterName to 2, indicating we are looking at pages per user. So, we need to only count the pages for a particular user. So, we check to see if item usernameOrPrinterName, or 2, of theCurrentLineContentsList matches theCurrentThing. If it does, then we want to count this line as a page printed by that user, (page_log uses one line per page printed), and increment our page count, theTotalCountedThings. Once we have gone through thePageLogContentsList, we reset AppleScript's text item delimiters to their default. Next we set the end of theTotalCountedThingsList to theTotalCountedThings, and set theTotalCountedThings back to zero, so we can get the page count for the next user name in theListedThing list. Once we have completely run through that list, we then return theTotalCountedThingsList to the calling line, and exit the handler:

on getTheList(theListedThing, thePageLogContentsList, usernameOrPrinterName)
   repeat with x from 1 to (length of theListedThing) --let's get the total pages by user first
      set theCurrentThing to item x of theListedThing --set the current user we are looking at
      repeat with y from 1 to (length of thePageLogContentsList) --now we want to get total 
         user pages by user
         set theCurrentLine to item y of thePageLogContentsList --grab a line
         set oldDelims to AppleScript's text item delimiters --temp store Applescript current 
         set AppleScript's text item delimiters to " " --use space as the current delimiter
         set theCurrentLineContentsList to (every text item of theCurrentLine) --make the line a list
         if item 1 of theCurrentLineContentsList is not "" then --there's always a blank line to 
            blow things up
            if item usernameOrPrinterName of theCurrentLineContentsList = theCurrentThing then 
               --if the user we're looking at printed this
               set theTotalCountedThings to theTotalCountedThings + 1 --increment the page count 
                  for the user
            end if
         end if
         set AppleScript's text item delimiters to oldDelims --reset the delimiters
      end repeat
      set the end of theTotalCountedThingsList to theTotalCountedThings --build the list
      set theTotalCountedThings to 0 --clear the var
   end repeat
   return theTotalCountedThingsList
end getTheList

Back in the main body of the code, we call this handler again, only to get the printer pages. The code works exactly the same, as in fact, it's the same handler. The only difference is which field in the page_log file we're dealing with.

Now that we've collected a bunch of data, we need to create the ouput file for it. First, we ask the person running the script where they want to save the file, and what they want to call it. This is done with choose file name, which gives us an alias to a file that doesn't exist yet.

set thePrinterReportFilePath to choose file name with prompt -
   "Pick the location for the printer report file" default name "Printer Report 
      File.txt" --user interaction, pick where you want the output file to go, and 
      give it a name

Then we set the variable theWrite to a nice long text string with the data we already have, such as total number of printers, total number of users, and total pages printed from this machine. Each line is tab-delimited with the "\t" character, which doesn't show up on screen once the script is saved, and each line is created with the "\r" character, which is invisible once the script is saved, and interpreted as a return:

set theWrite to ("Total number of Printers   " & (length of thePrinterTypeList) & "
Total number of Users   " & (length of theListedUsers) & "
Total number of pages for all printers   " & totalPagesForMachine) as text --get the non-user, 
   non-specific things, dump it into a text string var

But we still want a few more things, like pages per printer, etc. in the report. So, we set theListedThing to thePrinterTypeList, and theListedThingPages to pagesByPrinterList. We use the usernameOrPrinterName variable again, setting it to 0, to show the next handler that we are getting pages per printer. We then set theReport to the result of the handler getDataByPrinterOrUser(theListedThing, theListedThingPages, theWrite, usernameOrPrinterName):

set theListedThing to thePrinterTypeList
set theListedThingPages to pagesByPrinterList
set usernameOrPrinterName to 0 --we're going to get how many pages each printer 
   printed, and dump it into a text string
set theReport to getDataByPrinterOrUser(theListedThing, theListedThingPages, theWrite, 
   usernameOrPrinterName) of me --get pages by printer, and glom it on the end of theReport

The getDataByPrinterOrUser(theListedThing, theListedThingPages, theWrite, usernameOrPrinterName) Handler

The handler we use here is fairly simple. First, we check to see if we are doing printers or reports:

if usernameOrPrinterName = 0 then --which one are we looking at
      set userOrPrinter to "Printer"
      set userOrPrinter to "User"
   end if

Next, we run a repeat loop to iterate through theListedThing. We set theCurrentThing to the current user or printer name in theListedThing. We then set theCurrentThingPages to the same item in a different list, theListedThingPages. (The only reason this works is that thanks to the way CUPS sets up its page_log file, and the way we have gotten the data from that file, the lists synchronize nicely. I know this is a rather fragile way of doing things, but for now it works, and saved me a lot of work when I wrote this script. Obviously, anytime CUPS or Mac OS X get updated, the script should be tested to see when this finally breaks.) Then we concatenate a new line onto the end of theWrite, which gives us a tab-delimited line that shows "Printer" <tab> theCurrentThing <tab>"total pages"<tab> theCurrentThingPages. By placing the return character in front of the word "Printer" we ensure that each time we run through this list, we are creating a new line. Once we've run through theListedThing, the repeat loop exits. We clear out theListedThing and theListedThingPages, return theWrite, and that's the end of the handler:

repeat with x from 1 to (length of theListedThing) --run through the list of 
   printers or users
      set theCurrentThing to item x of theListedThing --grab the first printer or username
      set theCurrentThingPages to item x of theListedThingPages --grab the first page count by 
         user or printer
      (*This is to work around some annoyances with records that make it easier to use two lists. 
         As it turns out, positionally, everything lines up. I imagine this will bite me one day)
      set theWrite to theWrite & "
" & userOrPrinter & " " & theCurrentThing & " total pages   " & theCurrentThingPages --append pages 
   for a printer or a user in 
      --tab - delimited format 
   end repeat
   set theListedThing to {} --clear the var
   set theListedThingPages to {} --clear the var
   return theWrite --return the new text data
end getDataByPrinterOrUser

Now, we reuse the handler to get pages per user into our report in the correct format:

set theListedThing to theListedUsers
set theListedThingPages to pagesByUserList
set theWrite to theReport --theWrite doesn't update completely when you jack it between 
   handlers, so this fixes that
set usernameOrPrinterName to 1 --we're going to get how many pages each user printed, and 
   dump it into a text string
set theReport to getDataByPrinterOrUser(theListedThing, theListedThingPages, theWrite, 
   usernameOrPrinterName) of me --get pages by user, and glom it on the end of theReport

Okay, we've got our report. Let's write it out to a text file! First, we set thePrinterReportFile to the file handle returned by the open for access function. Since we are writing to the file, we obviously want to open the file, (using thePrinterReportFilePath we got from the user earlier) with write access. We then write the data in theReport to thePrinterReportFile, and then close the file handle. Almost done!

We could just end the script there, but face it, what do you do with a tab-delimited text file? Well, you mostly use it in other applications, such as FileMaker Pro, or Microsoft Excel. So let's save the user some time, and deal with this now.

First we set up a nice dialog box that tells the user the report has been created and where. We also allow them the option of inserting this report into FileMaker Pro or Microsoft Excel. We also, of course, allow them to do nothing, end the script and get on with other things. We set theProcessingRecord to the record returned from display dialog:

set theProcessingRecord to display dialog "The Printer Report has been created at:
" & (thePrinterReportFilePath as text) & "
Please click the button that corresponds to the application
you would like this file opened in" buttons {"Microsoft Excel", "Filemaker Pro", "None"} 
   default button "None" with icon note --nice option if you want to process this file right away

Inserting into Microsoft Excel or FileMaker Pro

When you are dealing with scripting any of the Microsoft Office applications, other than Entourage, you have to deal with a rather odd dictionary setup, primarily because things like Excel can be scripted in either AppleScript or Visual Basic for Applications (VBA). To make things easier, Microsoft had to make some unique decisions in the dictionaries for Excel and Word, so they do look a little odd. Excel's dictionary is quite useful, you just have to wrap your head around it.

The biggest oddity that affects the script is that Excel really can't handle aliases, so we have to convert the alias to a text string, then tell Excel to open it:

if button returned of theProcessingRecord = "Microsoft Excel" then --dump it into Excel
      tell application "Microsoft Excel"
         open (thePrinterReportFilePath as text) --Open can't handle aliases, so we have to 
            use a text version
      end tell
   end try

FileMaker Pro is a bit simpler, since it can handle aliases:

else if button returned of theProcessingRecord = "Filemaker Pro" then --dump the 
   file into FMPro
      tell application "FileMaker Pro"
         open thePrinterReportFilePath
      end tell
   end try
end if

If they don't pick either Excel or FileMaker Pro, then we leave the file alone, and the script is done.


Well, that's a lot of list work, but you get a neat, and possibly useful result. Printers can be a huge expense if their use isn't monitored closely. Thanks to CUPS, Mac OS X and AppleScript give you a way to do this. Now, there are still some things we could do with the script, such as trying to get pages per printer per user, etc., but this is a good start. As well, some judicious shell commands could make this script much shorter, and probably faster. iIt's not that long now, though, and my tests didn't show that it took all that long to run on an 800MHz PowerBook G4, so it's probably okay as it is. If nothing else, consider this an example of how AppleScript is still quite useful to the network administrator in Mac OS X.

John Welch <> is a Technical Strategist for Provar, ( and the Chief Know-It-All for TackyShirt, ( He has over fifteen years of experience at making Macs, and other computers work. John specializes in figuring out ways to make the Mac do what nobody thinks it can, showing that the Mac is a superior administrative platform, and teaching others how to use it in interesting, if sometimes frightening ways. He also does things that don't involve computertry on occasion, or at least that's the rumor.


Community Search:
MacTech Search:

Software Updates via MacUpdate

Microsoft Remote Desktop 8.0.16 - Connec...
With Microsoft Remote Desktop, you can connect to a remote PC and your work resources from almost anywhere. Experience the power of Windows with RemoteFX in a Remote Desktop client designed to help... Read more
Spotify - Stream music, create...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
djay Pro 1.1 - Transform your Mac into a...
djay Pro provides a complete toolkit for performing DJs. Its unique modern interface is built around a sophisticated integration with iTunes and Spotify, giving you instant access to millions of... Read more
Vivaldi - Lightweight browser...
Vivaldi browser. In 1994, two programmers started working on a web browser. Our idea was to make a really fast browser, capable of running on limited hardware, keeping in mind that users are... Read more
Stacks 2.6.11 - New way to create pages...
Stacks is a new way to create pages in RapidWeaver. It's a plugin designed to combine drag-and-drop simplicity with the power of fluid layout. Features: Fluid Layout: Stacks lets you build pages... Read more
xScope 4.1.3 - Onscreen graphic measurem...
xScope is powerful set of tools that are ideal for measuring, inspecting, and testing on-screen graphics and layouts. Its tools float above your desktop windows and can be accessed via a toolbar,... Read more
Cyberduck 4.7 - FTP and SFTP browser. (F...
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more
Labels & Addresses 1.7 - Powerful la...
Labels & Addresses is a home and office tool for printing all sorts of labels, envelopes, inventory labels, and price tags. Merge-printing capability makes the program a great tool for holiday... Read more
teleport 1.2.1 - Use one mouse/keyboard...
teleport is a simple utility to let you use one single mouse and keyboard to control several of your Macs. Simply reach the edge of your screen, and your mouse teleports to your other Mac! The... Read more
Apple iMovie 10.0.8 - Edit personal vide...
With an all-new design, Apple iMovie lets you enjoy your videos like never before. Browse your clips more easily, instantly share your favorite moments, and create beautiful HD movies and Hollywood-... Read more

Use Batting Average and the Apple Watch...
Batting Average, by Pixolini, is designed to help you manage your statistics. Every time you go to bat, you can use your Apple Watch to track  your swings, strikes, and hits. [Read more] | Read more »
Celebrate Studio Pango's 3rd Annive...
It is time to party, Pangoland pals! Studio Pango is celebrating their 3rd birthday and their gift to you is a new update to Pangoland. [Read more] | Read more »
Become the World's Most Important D...
Must Deliver, by cherrypick games, is a top-down endless-runner witha healthy dose of the living dead. [Read more] | Read more »
SoundHound + LiveLyrics is Making its De...
SoundHound Inc. has announced that SoundHound + LiveLyrics, will be one of the first third-party apps to hit the Apple Watch. With  SoundHound you'll be able to tap on your watch and have the app recognize the music you are listening to, then have... | Read more »
Adobe Joins the Apple Watch Lineup With...
A whole tidal wave of apps are headed for the Apple Watch, and Adobe has joined in with 3 new ways to enhance your creativity and collaborate with others. The watch apps pair with iPad/iPhone apps to give you total control over your Adobe projects... | Read more »
Z Steel Soldiers, Sequel to Kavcom'...
Kavcom has released Z Steel Soldiers, which continues the story of the comedic RTS originally created by the Bitmap Brothers. [Read more] | Read more »
Seene Lets You Create 3D Images With You...
Seene, by Obvious Engineering, is a 3D capture app that's meant to allow you to create visually stunning 3D images with a tap of your finger, and then share them as a 3D photo, video or gif. [Read more] | Read more »
Lost Within - Tips, Tricks, and Strategi...
Have you just downloaded Lost Within and are you in need of a guiding hand? While it’s not the toughest of games out there you might still want some helpful tips to get you started. [Read more] | Read more »
Entertain Your Pet With Your Watch With...
The Petcube Camera is a device that lets you use live video to check in on your pet, talk to them, and play with them using a laser pointer - all while you're away. And the Petcube app is coming to the Apple Watch, so you'll be able to hang out with... | Read more »
Now You Can Manage Your Line2 Calls With...
You'll be able to get your Line2 cloud phone service on the Apple Watch very soon. The watch app can send and receive messages using hands-free voice dictation, or by selecting from a list of provided responses. [Read more] | Read more »

Price Scanner via

Intel Compute Stick: A New Mini-Computing For...
The Intel Compute Stick, a new pocket-sized computer based on a quad-core Intel Atom processor running Windows 8.1 with Bing, is available now through Intel Authorized Dealers across much of the... Read more
Heal to Launch First One-Touch House Call Doc...
Santa Monica, California based Heal, a pioneer in on-demand personal health care services — will offer the first one-touch, on-demand house call doctor app for the Apple Watch. Heal’s Watch app,... Read more
Mac Notebooks: Avoiding MagSafe Power Adapter...
Apple Support says proper usage, care, and maintenance of Your Mac notebook’s MagSafe power adapter can substantially increase the the adapter’s service life. Of course, MagSafe itself is an Apple... Read more
12″ Retina MacBook In Shootout With Air And P...
BareFeats’ rob-ART morgan has posted another comparison of the 12″ MacBook with other Mac laptops, noting that the general goodness of all Mac laptops can make which one to purchase a tough decision... Read more
FileMaker Go for iPad and iPhone: Over 1.5 Mi...
FileMaker has announced that its FileMaker Go for iPad and iPhone app has surpassed 1.5 million downloads from the iTunes App Store. The milestone confirms the continued popularity of the FileMaker... Read more
Sale! 13-inch 2.7GHz Retina MacBook Pro for $...
 Best Buy has the new 2015 13″ 2.7GHz/128GB Retina MacBook Pro on sale for $1099 – $200 off MSRP. Choose free shipping or free local store pickup (if available). Price for online orders only, in-... Read more
Minimalist MacBook Confirms Death of Steve Jo...
ReadWrite’s Adriana Lee has posted a eulogy for the “Digital Hub” concept Steve Jobs first proposed back in 2001, declaring the new 12-inch MacBook with its single, over-subscribed USB-C port to be... Read more
13-inch 2.7GHz Retina MacBook Pro for $1234 w...
Adorama has the 13″ 2.7GHz/128GB Retina MacBook Pro in stock for $1234.99 ($65 off MSRP) including free shipping plus a free LG external DVD/CD optical drive. Adorama charges sales tax in NY & NJ... Read more
13-inch 2.5GHz MacBook Pro available for $999...
 Adorama has the 13-inch 2.5GHz MacBook Pro on sale for $999 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP. Read more
Save up to $600 with Apple refurbished Mac Pr...
The Apple Store is offering Apple Certified Refurbished Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Support Technician IV - Jack Henry a...
Job Description Jack Henry & Associates is seeking an Apple Support Technician. This position while acting independently, ensures the proper day-to-day control of Read more
*Apple* Client Systems Solution Specialist -...
…drive revenue and profit in assigned sales segment and/or region specific to the Apple brand and product sets. This person will work directly with CDW Account Managers Read more
*Apple* Software Support - Casper (Can work...
…experience . Full knowledge of Mac OS X and prior . Mac OSX / Server . Apple Remote Desktop . Process Documentation . Ability to prioritize multiple tasks in a fast pace Read more
*Apple* Software Support - Xerox Corporation...
…Imaging experience Full knowledge of Mac OS X and prior Mac OSX / Server Apple Remote Desktop Process Documentation Ability to prioritize multiple tasks in a fast pace Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.