How to interpret status code in Python commands.getstatusoutput()

16,798

Solution 1

There is a set of functions in os module (os.WIFCONTINUED, os.WIFSTOPPED, os.WTERMSIG, os.WCOREDUMP, os.WIFEXITED, os.WEXITSTATUS, os.WIFSIGNALED, os.WSTOPSIG), which correspond to macros from wait(2) manual. You should use them to interpret the status code.

For example, to get the exit code you should use os.WEXITSTATUS(status)

A better idea would be to switch to subprocess module.

Solution 2

Wow. The insight that it was multiplying by 256 got me there. Searching for "python commands +256" got me to a Python Module Of The Week article which explains what's going on.

Here's a snippet from that page:

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.

And some of Doug's code:

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')

Solution 3

Looking at commands.py:

def getstatusoutput(cmd):
    """Return (status, output) of executing cmd in a shell."""
    import os
    pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r')
    text = pipe.read()
    sts = pipe.close()
    if sts is None: sts = 0
    if text[-1:] == '\n': text = text[:-1]
    return sts, text

We see sts holds the value of os.popen(...).close(). Looking at that documentation, os.popen(...).close() returns the value of os.wait:

os.wait()

Wait for completion of a child process, and return a tuple containing its pid and exit status indication: a 16-bit number, whose low byte is the signal number that killed the process, and whose high byte is the exit status (if the signal number is zero); the high bit of the low byte is set if a core file was produced. Availability: Unix.

Emphasis was mine. I agree that this "encoding" isn't terribly intuitive, but at least it was fairly obvious at a glance that it was being multiplied/bit-shifted.

Share:
16,798
Shrey Shivam
Author by

Shrey Shivam

Geek. Hiker. Cook. Voracious reader. Sometime writer. Python, BASH, Linux, command-line in general -- all deep geeky loves of mine. (Currently learning C.)

Updated on June 07, 2022

Comments

  • Shrey Shivam
    Shrey Shivam almost 2 years

    In a related question, I asked where to find the documentation for the C function "wait." This was an attempt to figure out return codes for the commands.getstatusoutput() module. Stackoverflow came through, but the documentation didn't help. Here's what puzzles me:

    #!/usr/bin/python
    import commands
    goodcommand = 'ls /'
    badcommand = 'ls /fail'
    status, output = commands.getstatusoutput(goodcommand)
    print('Good command reported status of %s' % status)
    status, output = commands.getstatusoutput(badcommand)
    print('Bad command reported status of %s' % status)
    

    When run on OS X (Leopard) I get the following output: (Which matches the documentation.)

    $ python waitest.py 
    Good command reported status of 0
    Bad command reported status of 256
    

    On OS X, doing an "ls /fail ; echo $?" gets the following output:

    $ ls /fail ; echo $?
    ls: /fail: No such file or directory
    1
    

    When run on Linux (Ubuntu Hardy) I get the following output:

    $ python waitest.py 
    Good command reported status of 0
    Bad command reported status of 512
    

    On Ubuntu, doing "ls /fail" gets a 2:

    $ ls /fail ; echo $?
    ls: cannot access /fail: No such file or directory
    2
    

    So Python appears to be multiplying status codes by 256. Huh? Is this documented somewhere?

  • CaffeineAddiction
    CaffeineAddiction over 8 years
    the return of os.system(cmd) seems to work the same way insight that it was multiplying by 256
  • Ivan X
    Ivan X over 8 years
    Thank you for the emphasis, I was pretty confused by what I was seeing until I read this..