Table of Contents

PS Package

What is it?

The ps package is a couple of simple classes I created to wrap a system call to /bin/ps on a *nix machine. This will parse the output of that call and give you an easy to use object that has some simple accessor functions for finding what you want in the ps output or by letting you iterate over the object itself and accessing the information for each running process.

The Script

#!/usr/bin/env python
 
# $Id: ps.py,v 1.5 2008/04/24 03:37:05 jay Exp $
 
"""
This will run a system call of '/bin/ps auxww' and parse the results and put
them in object containers for easy access to the data.
 
Usage:
 
    import ps
    myps = ps.PS()
    for row in myps:
        for col in row:
            print '%s: %s' % (col , row[col])
 
    # You can also access the row items by name
    for row in myps:
        print row.pid    # OR...
        print row['pid']
 
    # You can search for a particular process or processes
    rows = myps.getProcByName('firefox(?:-bin)?')
    for row in rows:
        print '%s: %s' % (row.pid , row.command)
 
    # Get all the processes for a given user
    rows = myps.getByUser('username')
    for row in rows:
        print '%s: %s' % (row.pid , row.command)
 
    # Get the info for a given pid number
    pidNum = open('/var/run/myproc.pid').read()
    row = myps.getByPid(pidNum)
    if row:
        print '%s: %d' % (row.command , row.cpu_perc)
"""
 
import os , re
 
__all__ = ['PS' , 'PSRow']
 
class PS(object):
    """
    This is the callable class.  This will create an array of accessible
    PSRow objects that contain all the info for current ps output.
    """
    def __init__(self , psPath='/bin/ps' , psArgs='auxww'):
        """
        psPath: This should be the path to the ps binary on your system.
            default: /bin/ps
 
        psArgs: These are the args to be used when calling ps.  See "man ps"
                for more information.
            default: auxww
        """
        self._rows = []
        self._rawCols = []
        self._psPath = psPath
        self._psArgs = psArgs
        self._cmd = '%s %s' % (self._psPath , self._psArgs)
        self._curNum = 0
        self.refresh()
 
    def _runCommand(self):
        """
        This actually runs the command and creates the array
        """
        ps = os.popen(self._cmd)
        self._setRawCols(ps.readline())
        while True:
            line = ps.readline()
            if not line: break
            self._rows.append(PSRow(line , self._rawCols))
        ps.close()
 
    def getRows(self):
        """
        getRows() -> Returns a list of PSRow objects
        """
        return self._rows
    Rows = property(getRows)
 
    def getRawCols(self):
        """
        getRawCols() -> Returns a list of strings pertaining to the column
                        headers
        """
        return self._rawCols
    def _setRawCols(self , line):
        for c in re.split(r'\s+' , line.strip()):
            if '%' in c:
                c = c.replace('%' , '') + '_perc'
            self._rawCols.append(c.lower())
    RawCols = property(getRawCols)
 
    def getByPid(self , pid):
        """
        Returns a PSRow object pertaining to the pid number or None if it
        does not exist
        """
        for row in self._rows:
            if 'pid' not in row:
                return None
            if int(row.pid) == int(pid):
                return row
        return None
 
    def getByUser(self , username):
        """
        Returns a list of PSRow objects for the given username
        """
        ret = []
        for row in self._rows:
            if 'user' not in row:
                return None
            if row.user == username:
                ret.append(row)
        return ret
 
    def getProcByName(self , search):
        """
        Returns a list of PSRow objects based on a search of the executed
        commands given the PCRE string "search".
        """
        ret = []
        reg = re.compile(search)
        for row in self._rows:
            if 'command' not in row:
                return None
            if reg.search(row.command):
                ret.append(row)
        return ret
 
    def refresh(self):
        """
        Refreshes the current rows of ps output by performing another run of
        ps.
        """
        self._rows = []
        self._rawCols = []
        self._runCommand()
 
    def __iter__(self):
        """
        Makes the PS object iterable
        """
        return PS._iter(self)
 
    def _iter(self):
        """
        Iterates over the rows of PSRow objects for the given instance
        """
        for psr in self._rows: yield psr
 
class PSRow(object):
    """
    This is a data structure holding all the results of a row of ps output
    """
    def __init__(self , s , cols):
        """
        s: The string pertaining to one row of ps output
        cols: A list of the column header names
        """
        self.cols = cols
        self.items = {}
        self._parseStr(s)
 
    def _parseStr(self , s):
        """
        Parses the ps output row string into members of this object
        """
        items = re.split(r'\s+' , s.strip() , len(self.cols) - 1)
        self.items = dict(zip(self.cols , items))
        self.__dict__.update(self.items)
 
    def __contains__(self , k):
        """
        Returns true if the string, k, pertains to a member of this object
        For example, if you wanted to know if there was a "user" column
        available from the ps output you could do the following:
 
            if 'user' in psrow:
                print psrow.user
        """
        return k in self.items
 
    def __iter__(self):
        """
        Makes the PSRow object iterable
        """
        return PSRow._iter(self)
 
    def _iter(self):
        """
        Iterates over the keys in the PSRow object
        """
        for c in self.items.keys(): yield c
 
    def __getitem__(self , name):
        """
        Allows for dictionary access to the row items:
 
            print psrow['user']
        """
        if name in self.items:
            return self.items[name]

Usage

This is meant to be used purely as a library. Here is a simple example. Run pydoc ps for more information.

import ps
 
myps = ps.PS()
# Show each running process as a block
for row in myps:
    for col, val in row.items():
        print '%s: %s' % (col , val)
    print

After installing, run pydoc ps for more information.

Install

Simply download the script or copy the above into a file and put the file in your lib/python<version>/site-packages directory. This is usually something to the effect of /usr/lib/python2.4/site-packages but it varies depending on the distro.