PyMOTW: commands

The commands module contains utility functions for working with shell command output under Unix.

Module: commands
Purpose: Run external shell commands and capture the status code and output.
Python Version: 1.4

Description:

Note: This module is made obsolete by the subprocess module.

There are 3 functions in the commands module for working with external commands. The functions are shell-aware and return the output or status code from the command.

getstatusoutput():

The function getstatusoutput() runs a command via the shell and returns the exit code and the text output (stdout and stderr combined). The exit codes are the same as for the C function wait() or os.wait(). The code is a 16-bit number. The low byte contains the signal number that killed the process. When the signal is zero, the high byte is the exit status of the program. If a core file was produced, the high bit of the low byte is set.

from commands import *

def run_command(cmd):
print 'Running: "%s"' % cmd
status, text = getstatusoutput(cmd)
exit_code = status >> 8
signal_num = status % 256
print 'Signal: %d' % signal_num
print 'Exit : %d' % exit_code
print 'Core? : %s' % bool(exit_code / 256)
print 'Output:'
print text
print

run_command('ls -l *.py')
run_command('ls -l *.notthere')
run_command('echo "WAITING TO BE KILLED"; read input')


This example runs 2 commands which exit normally, and a third which blocks waiting to be killed from another shell. (Don’t simply use Ctrl-C as the interpreter will intercept that signal. Use ps and grep in another window to find the read process and send it a signal with kill.)

$ python commands_getstatusoutput.py
Running: "ls -l *.py"
Signal: 0
Exit : 0
Core? : False
Output:
-rw-r--r-- 1 dhellman dhellman 1191 Oct 21 09:41 __init__.py
-rw-r--r-- 1 dhellman dhellman 1321 Oct 21 09:48 commands_getoutput.py
-rw-r--r-- 1 dhellman dhellman 1265 Oct 21 09:50 commands_getstatus.py
-rw-r--r-- 1 dhellman dhellman 1626 Oct 21 10:10 commands_getstatusoutput.py

Running: "ls -l *.notthere"
Signal: 0
Exit : 1
Core? : False
Output:
ls: *.notthere: No such file or directory

Running: "echo "WAITING TO BE KILLED"; read input"
Signal: 1
Exit : 0
Core? : False
Output:
WAITING TO BE KILLED


In this example, I used “kill -HUP $PID” to kill the read process.

getoutput():

If the exit code is not useful for your application, you can use getoutput() to receive only the text output from the command.

from commands import *

text = getoutput('ls -l *.py')
print 'ls -l *.py:'
print text

print

text = getoutput('ls -l *.notthere')
print 'ls -l *.py:'
print text


$ python commands_getoutput.py      
ls -l *.py:
-rw-r--r-- 1 dhellman dhellman 1191 Oct 21 09:41 __init__.py
-rw-r--r-- 1 dhellman dhellman 1321 Oct 21 09:48 commands_getoutput.py
-rw-r--r-- 1 dhellman dhellman 1265 Oct 21 09:50 commands_getstatus.py
-rw-r--r-- 1 dhellman dhellman 1626 Oct 21 10:10 commands_getstatusoutput.py

ls -l *.py:
ls: *.notthere: No such file or directory


getstatus():

Contrary to what you might expect, getstatus() does not run a command and return the status code. Instead, it’s argument is a filename which is combined with “ls -ld” to build a command to be run by getoutput(). The text output of the command is returned.

from commands import *

status = getstatus('commands_getstatus.py')
print 'commands_getstatus.py:', status
status = getstatus('notthere.py')
print 'notthere.py:', status
status = getstatus('$filename')
print '$filename:', status


As you notice from the output, the $ character in the argument to the last call is escaped so the environment variable name is not expanded.

 $ python commands_getstatus.py
commands_getstatus.py: -rw-r--r-- 1 dhellman dhellman 1387 Oct 21 10:19 commands_getstatus.py
notthere.py: ls: notthere.py: No such file or directory
$filename: ls: $filename: No such file or directory


References:

Python Module of the Week Home
Download Sample Code


Technorati Tags:
,


  • http://www.algorithm.co.il/blogs lorg

    I really like your PyMOTW series, but I don’t understand why cover the commands module if it is deprecated?
    (More so if it is for such an antiquated version of Python).
    Having it in the documentation might be good if someone happens to see some code using it, but I see no reason teaching its use.

  • http://www.blogger.com/profile/01892352754222143463 Doug

    That’s a reasonable point. I decided to go ahead and cover it because the module is still present in Python 2.5 and my goal has been to work through all of the current modules. Since existing code might still use it, understanding how it works can be valuable.

  • http://www.blogger.com/profile/13609779138164842374 Graham Dumpleton

    Extra tip to go with use of ‘commands’ module. Under the covers it uses the popen() call. When the subsequent descriptor is closed and the system pclose() is called, it will wait for the exit status of the subprocess created. To get the exit status popen() would in general use the wait4() or equivalent system call, which relies on the parent process being able to receive a SIGCHLD signal when the subprocess exits. If for some reason you have set up the SIGCHLD signal to be ignored, the popen() call will fail and the close of the descriptor will as a result throw a Python exception.

    This isn’t universal though, as on MacOS X it looks like you can ignore SIGCHLD and it still works. Ignore SIGCHLD on Linux though and it will definitely fail.

  • http://www.blogger.com/profile/01892352754222143463 Doug

    Thanks, Graham, that’s an excellent tip!