Archive for the ‘Snippets’ Category

python subprocess over shell

Monday, May 10th, 2010

One of my common gripes is when people struggle with complicated shell scripts that would be much simpler in a scripting language like Python, Ruby or Perl. I used to abuse PHP for this, but saw the light.

If you’re talking about replacing shell scripts, all of these are pretty much equivalent, but I don’t really do Perl for no particular reason, I’m a big fan of Python for general purpose work just because of the rich module system, and at least “dozens of engineers” use it at work..

The reason we generally write shell scripts is because we want to execute a bunch of external processes in an automated way.

In this area you can whip something basic up fastest in shell, yes, but at some point you’re going to have to repay that technical debt if you need to get past a certain point.

Besides, it’s not like shell scripting always stays simple and easy… and the overhead of moving to a more powerful language isn’t that huge.

If I’m sure the scope of a script will be small, or I don’t have the option of moving to structured data format for input and output with external commands, or I don’t have to futz around with arrays, I’ll stick with bash.

But, if I want to work with dictionary objects or talk in a protocol like LDAP or model things as objects, or need complex handling and passing around of stdin/stdout/exit statuses, or know some module handles lots of edge cases for me, I’ll move to Python or Ruby. I quite like both, but feel that Python is more utilitarian, and simply due to whitespace enforcement and extensive linters is a good fit for code that may need to be picked up and understood quickly by a co-worker.

When it comes to getting started with Python, I still suggest Dive into Python for people. Just flipping through Chapters 1-3 equips you with an awful lot.

Anyway, some people think Python is hard, but it’s not really. I think of Python as being extremely utilitarian, which makes it a great fit for sysadmin work.

Someone posted on the MacEnterprise list a question about working out what PPD was in use by each printer on the system in OS X, and someone gave a good shell example using the usual suspects of for, grep and awk.

There’s nothing really wrong with doing this, but I’ve come to distrust parsing non-structured output if I need to keep this solution working across many multiple versions of operating systems, or even between OS X major versions given Apple’s history. One of the big advantages of moving to Python is being able to parse and manipulate property lists, which can get really painful in shell. You end up writing out lots of temp files or giving up on dealing with error conditions.

Anyway, so lets see what it’s like executing a command in Python with the subprocess module. We’re asking System Profiler for printer info, and telling it to return the output in XML plist format.

#!/usr/bin/python

import subprocess

command = [‘system_profiler’, ‘-xml’, ‘SPPrintersDataType’]
task = subprocess.Popen(command,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)

(stdout, stderr) = task.communicate()
print stdout

To skim through those lines, we’re

  • setting the python shebang
  • importing the subprocess module
  • defining a list called ‘command’ to store the command we want to run
  • creating a subprocess Popen object to run our command called ‘task’
  • setting standard out and standard error to go to our own pipes
  • getting standard out and standard error from the task.
  • printing standard out

This really isn’t much work, and if you really only wanted this functionality, there are other convenience functions that you can use to make this even shorter, or write your own convenience function.

But we have all sorts of options now.

We can send standard input to the task:

(stdout, stderr) = task.communicate(stdin)

We can ask what the exit status is easily:

status = task.returncode

and if we get None we know the process hasn’t terminated yet.

And if we want to do something a bit more complicated we can search through the structured data plist output easily like:

#!/usr/bin/python

import plistlib
import subprocess

command = [‘system_profiler’, ‘-xml’, ‘SPPrintersDataType’]
task = subprocess.Popen(command,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)

(stdout, stderr) = task.communicate()
printers = plistlib.readPlistFromString(stdout)
printers = printers[0][‘_items’]

for printer in printers:
  print ‘Name: ‘ + printer[‘_name’]
  print ‘PPD: ‘ + printer[‘ppd’]
 

This is easier to extend than the standard for/grep/awk/sed equivalents tend to be, and much easier for a co-worker to pick up and understand. It comes close to documenting itself, just needs some comments about the structure of Apple’s output, and some try/except blocks in case the output is malformed or does change.

useful screen bash function

Wednesday, May 14th, 2008

So I pretty much live in screen permanently these days, and there are a gazillion awesome introductions out there to screen that describe the amazing advantages of screen.

One thing that has been a minor niggle has been the clunky way you pick which screen session you wish to reattach to if you have multiple sessions active, so I whipped up this bash function that I’ve been finding really useful.

function scr() {

  if [ ! $1 ]; then

    screen -D -R

  else

    i=1

    for j in $(screen -list | awk --posix '/^[[:space:]]*[[:digit:]]{2,}.*$/{print $1}'); do

      screens[${i}]=$j

      let i=i+1

    done

    # test if integer

    if ! [ $1 -eq $1 2> /dev/null ]; then

      echo "You must supply an integer as the argument"

    elif [ $1 -eq 0 ]; then

      echo "screens list count starts at 1, not zero."

    elif [[ ${1} -gt ${#screens[@]} ]]; then

      echo "only ${#screens[@]} screens exist"

      screen -list

    else

      screen -D -R ${screens[$1]}

    fi

  fi

}

So you can basically go scr to reattach to a single screen session, scr 1 to reattach to the first, scr 2 to reattach to the second, etc etc.

I’m kind of expecting someone to pipe up now and point out a much easier way… :)

Creating stable and unstable branches with Apple’s Software Update Server.

Thursday, August 23rd, 2007

Creating stable and unstable branches with Apple’s Software Update Server @ afp548.com

Heading to MacWorld…

Friday, December 22nd, 2006

Just as a quick note…

After spending four (maybe five if England can hold out that long) glorious days watching Shane Warne’s last Test match at the SCG, I’ll be flying straight out to San Francisco to do some presentations at MacWorld.

It’s always good to put faces to names, so if you’re around make sure to say hi, and even more importantly, make sure you make it to the AFP548 Kick-Off Session at Macworld.

We can play the drinking game every time Joel’s voice does that squeaky Jay Leno thing… :)

Typos can be fatal…

Wednesday, October 18th, 2006

So last night I was playing around with the MBR on an external drive… and completely without thinking managed to wipe out my laptop.

/dev/rdisk1 Nigel, not /dev/rdisk0 ….

bleagh.

Anyway, it’s made me realise just how much I love HomeSync, as 95% of everything that matters has been syncing up to the server… and as I’ve had two requests now for a post on Mobile Accounts and HomeSync best practices, this is as good a time as any… working on it now while watching OS X and Win XP reinstall….

Airport Extreme base stations do not do Radius Accounting.

Thursday, October 13th, 2005

This is mainly for any weary souls treading the same Google path that I have been lately…

The Airport Extreme (and I assume the Express as well) base stations do not do Radius Accounting. They do happily work with Radius Authentication, but not Accounting.

This means that if you’re looking for a solution for network quotas on a wireless network, you’ll have to find some other way.