4

I need to execute echo $? using python3 and capture the exit status. I need this especially for capturing the Segmentation fault (core dumped) status. I tried :

>>> os.system('echo $?')
0
0

got 0 0. Also, for segfault,

>>> os.system('./a.out')
Segmentation fault (core dumped)
35584

After above command, I again got:

>>> os.system('echo $?')
0
0

Also, why is 0 getting printed twice? I went through the doccumentation of python-3 which says:

os.system(command)

On Unix, the return value is the exit status of the process encoded in the format specified for wait(). Note that POSIX does not specify the meaning of the return value of the C system() function, so the return value of the Python function is system-dependent.

Does this something say about such behavior? Help me clarify on this.

Note: I already ran the ulimit -c unlimited before all above steps. The expected result should be non-zero or 139(to be specific).

Edit: I am thinking if there is a limitation on this!

Thanks!

Community
  • 1
  • 1
Dr. Essen
  • 603
  • 2
  • 9
  • 25
  • 1
    Zero is being printed twice because `echo $?` within the shell echoes zero, then the Python REPL prints the return value of `os.system()`, which is the return code of the `echo` command. – AKX Jun 18 '18 at 06:13
  • @Allan, Thanks for the advice. :) And what if I figured out the answer same as the one which somebody has posted the same answer later on? – Dr. Essen Jun 19 '18 at 13:57

3 Answers3

2

No, you don't need to execute echo $?. It wouldn't be useful. The exit status of the program is the return value of the function os.system. That's what the number 35584 is. The documentation os os.system tells you to read the documentation of os.wait which explains

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.

However, note that depending on the shell, with os.system('./a.out'), you may be getting the exit status of a.out or the exit status of the shell itself. Normally there's no difference, because the exit status of the shell is the exit status of the last command it executes. But if the command dies from a signal, then there is a difference. The shell won't kill itself with the same signal, it will return a status that encodes the signal. In most shells, that's 128 + signal_number. For example, if the program dies of signal 11 (segfault on Linux) and leaves a core dump, then its status as returned by wait is 11. But if there's a shell in between, then the shell will exit normally with the exit code 128+11. That's what you're seeing: 35584 is (128 + 11) << 8.

To avoid this complication, use subprocess.call or one of its variants (you can use subprocess.run if you don't need your code to run Python <=3.4).

returncode = subprocess.call(['./a.out'], shell=False).returncode
if returncode & 0xff == 0:
    exit_code = returncode >> 8
    print('The program exited normally with status {}.'.format(exit_code))
else:
    print('The program was killed by signal {}.'.format(returncode))

If you run os.system('echo $?'), this starts a new shell. You're printing the initial value of $? in that shell, before it has run any command, and the initial value of $? in a shell is 0.

You see 0 twice in the interactive environment because the first one is the one printed by the echo command and the second one is the value of the Python expression. Compare os.system('echo hello').

Note that with os.system, you can't access the output of the command, so if you print something with echo, you can't use it in the program. You'd have to use functions in the subprocess module for that, but you need this only if you need the output of ./a.out, not to get its exit status.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
  • Thanks! for such a thorough information. After reading all the answers, I wrote the same code before you posted above using `subprocess`. But I can't see those answers below. Seems like they have been deleted! sad! :( – Dr. Essen Jun 18 '18 at 07:07
1

When running:

>>> os.system('echo $?')
0
0

and if your previous command was successful a first 0 will be print by echo $? and another one will be the return code of the call to echo $? that has just succeeded so you will have another 0 printed.

The return code of the script/command that you execute will directly be returned to your python program by os.system function so you do not need to use echo $?

Examples:

$ more return_code*
::::::::::::::
return_code1.py
::::::::::::::
import os

print os.system('sleep 1')
#will print 0 after 1sec
::::::::::::::
return_code2.py
::::::::::::::
import os

print os.system('ls abcdef')
#will print a rc!=0 if the file abcdef is not present in your working directory

Executions:

$ python return_code1.py 
0

and

$ python return_code2.py                                                                                                                                                                                                                                        
ls: cannot access 'abcdef': No such file or directory
512
Allan
  • 12,117
  • 3
  • 27
  • 51
0

I have written the following code for the above question and worked as expected. I used subprocess.Popen() method for achieving my requirement. I used sample.returncode to get the exit status of the shell.

def run_cmd():
    ret = 0
    sample_cmd = "./a.out"
    sample = subprocess.Popen(sample_cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out_stdout, out_stderr = sample.communicate()
    if sample.returncode != 0:
        print ("OUTPUT: %s\nERROR: %s\n"%(out_stdout, out_stderr))
        print ("Command:  %s  \nStatus: FAIL "%(sample_cmd))
        sys.stdout.flush()
        if sample.returncode == 139:
            print('Segmentation fauilt(core dumped) occured...with status: ', sample.returncode)
            ret = sample.returncode
        else:
            ret = 1
    else:
        print ("OUTPUT: %s\n"%(out_stdout))             
        print ("Command:  %s  \nStatus: PASS "%(sample_cmd))
        ret = 0
Dr. Essen
  • 603
  • 2
  • 9
  • 25