4

There are several ways of calling C++ executable programs. For example, we can use

 def run_exe_return_code(run_cmd):
        process=subprocess.Popen(run_cmd,stdout=subprocess.PIPE,shell=True)
        (output,err)=process.communicate()
        exit_code = process.wait()
        print output
        print err
        print exit_code
        return exit_code

to process a C++ executable program: run_exe_return_code('abc') while abc is created by the following C++ codes:

int main()
{
      return 1;
}

In the above codes, the return value of the program is 1, and if we run this Python script in Linux we can always see the return value by the Python script is 1. However, in Android environment it seems that the return exit code in the above python script is 0, which means successful. Is there a solution where the Python script can know the return value of main function in Android environment?

By the way, in android environment, I use adb shell abc instead of abc in order to run the program.

Cœur
  • 37,241
  • 25
  • 195
  • 267
feelfree
  • 11,175
  • 20
  • 96
  • 167
  • 1
    Can you describe a real example where this does not work? What makes you think 0 is not the right answer? – Reut Sharabani Jun 22 '15 at 16:07
  • @ReutSharabani It is very simple to test it. in main() function, you can do nothing but return 1. Then you just run run_exe_return_code python function, and the exit_code is 0. Then, if you change the return value in main() function to 0, and run the python script once again, and you will find the exit_code is still 0. – feelfree Jun 22 '15 at 16:10
  • @feelfree Then why not to make a *complete* example using `int main(){return 1;}` ? It would avoid confusion, and more importantly, would be immediately compilable. – luk32 Jun 22 '15 at 16:13
  • @PadraicCunningham any reason his code should not work? I've used the same function and it worked. Are there any drawbacks to it? – Reut Sharabani Jun 22 '15 at 16:23
  • 1
    @ReutSharabani, communicate is already blocking and waiting for the process to terminate, wait can also cause a deadlock https://docs.python.org/2/library/subprocess.html#subprocess.Popen.wait – Padraic Cunningham Jun 22 '15 at 16:24
  • @PadraicCunningham makes sense. in that case there is no point in using `wait`. – Reut Sharabani Jun 22 '15 at 16:26
  • @ReutSharabani yes, it is redundant really, you can use check_output as you suggest in your answer catching the CalledProcessError, you can get the error message and the returncode in the except or else just the output from a successful run of the process. – Padraic Cunningham Jun 22 '15 at 16:28

5 Answers5

4

For your android problem you can use fb-adb which "propagates program exit status instead of always exiting with status 0" (preferred), or use this workaround (hackish... not recommended for production use):

def run_exe_return_code(run_cmd):
        process=subprocess.Popen(run_cmd + '; echo $?',stdout=subprocess.PIPE,shell=True)
        (output,err)=process.communicate()
        exit_code = process.wait()
        print output
        print err
        print exit_code
        return exit_code

Note that the last process's code is echo-ed so get it from the output, not from the exit_code of adb.

$? returns the last exit code. So printing it allows you to access it from python.


As to your original question:

I can not reproduce this. Here is a simple example:

Content of .c file:

reut@reut-VirtualBox:~/pyh$ cat c.c 
int main() {
    return 1;
}

Compile (to a.out by default...):

reut@reut-VirtualBox:~/pyh$ gcc c.c

Content of .py file:

reut@reut-VirtualBox:~/pyh$ cat tstc.py 
#!/usr/bin/env python

import subprocess

def run_exe_return_code(run_cmd):
    process=subprocess.Popen(run_cmd,stdout=subprocess.PIPE)
    (output,err)=process.communicate()
    exit_code = process.wait()
    print output
    print err
    print exit_code

run_exe_return_code('./a.out')

Test:

reut@reut-VirtualBox:~/pyh$ ./tstc.py 

None
1

exit_code is 1 as expected.

Notice that the return value is always an integer. You may want the output which you can get by using subprocess.check_output:

Run command with arguments and return its output as a byte string.

Example:

>>> subprocess.check_output(["echo", "Hello World!"])
'Hello World!\n'

Note: If the return value is 1, which signals an error, a CalledProcessError exception will be raised (which is usually a good thing since you can respond to it).

Reut Sharabani
  • 30,449
  • 6
  • 70
  • 88
  • 1
    check_output would raise a CalledProcessError if the exit status was non-zero so you would have to catch the exception – Padraic Cunningham Jun 22 '15 at 16:20
  • This is written under the assumption he is looking for what was written to `stdout`, but thanks for the comment. – Reut Sharabani Jun 22 '15 at 16:21
  • 1
    Yes but if the code returns 1 you won't see output, the program will error. You also don't need shell=True – Padraic Cunningham Jun 22 '15 at 16:23
  • It's nice that `shell=True` got removed it's almost never a good idea. Also, the `check_call` should not be used as a method to capture the error code, but when a non-zero return code is an exceptional situation. It is confusing seeing `try...catch` semantics for normal operation (i.e. expecting the exception), when there exists an alternative which treats all return values equally. – luk32 Jun 22 '15 at 16:32
1

I think you can try commands.getstatusoutput, like this:

import commands

status, result = commands.getstatusoutput(run_cmd)

print result

Will
  • 792
  • 1
  • 5
  • 22
1

Yes, you can! The simple version of the code you submitted would be:

import subprocess
exit_code=subprocess.call('./a.out')`
print exit_code

with ./a.out the program compiled from:

int main(){
    return 3;
}

Test:

python testRun.py
3

Ah, and note that shell=True can be a security hazard. https://docs.python.org/2/library/subprocess.html

def run_exe_return_code(run_cmd):
    process=subprocess.Popen(run_cmd,stdout=subprocess.PIPE,shell=True)
Darius Zu CH
  • 145
  • 1
  • 8
  • which version of python are you using? Apparently, subprocess port on android was an issue. https://bugs.python.org/issue16255 https://code.google.com/p/python-for-android/source/browse/python3-alpha/python3-src/Lib/subprocess.py?r=b3c1049b5eadd74a307f917b3703ab6911165e4e – Darius Zu CH Jun 22 '15 at 16:55
0

See this answer: https://stackoverflow.com/a/5631819/902846. Adapting it to your example, it would look like this:

def run_exe_return_code(run_cmd):
    process = subprocess.Popen(run_cmd, stdout=subprocess.PIPE, shell=True)
    (output, err) = process.communicate()
    process.wait()
    print output
    print err
    print process.returncode
    return process.returncode

The short summary is that you can use Popen.wait, Popen.poll, or Popen.communicate as appropriate to cause the return code to be updated and then check the return code with Popen.returncode afterwards.

Also see the Python docs for Popen: https://docs.python.org/2/library/subprocess.html

Community
  • 1
  • 1
West 39th
  • 634
  • 3
  • 10
0
def run_exe_android_return_code(run_cmd):
     #adb shell '{your command here} > /dev/null 2>&1; echo $?'
     process=subprocess.Popen(run_cmd,stdout=subprocess.PIPE,shell=True)
     (output,err)=process.communicate()
     pos1   = output.rfind('\n')
     output = output[:pos1-1]
     pos2   = output.rfind('\n')
     output = output[pos2+1:]
     print output
     return output

This is the Python script that is used to check the return value of running an executable on Android.

def run_android_executable(full_path,executable):
    executable = full_path+'/'+executable
    run_cmd = 'adb shell \'LD_LIBRARY_PATH='+full_path+':$LD_LIBRARY_PATH '+executable+'; echo $?\''
    print run_cmd
    error_code=run_exe_android_return_code(run_cmd)
    print 'the error code is'
    print error_code
    if(error_code=='1'):
        print 'the executable returns error'
    else:
        print 'the exectuable runs smoothly'

This is the secript that is used to run the executable. It is a little different from Reut Sharabani's answer, and it works.

feelfree
  • 11,175
  • 20
  • 96
  • 167