8

I have a Python script that should report success or failure of the previous command. Currently, I'm doing

command && myscript "Success" || myscript "Failed"

What I would like to do is instead to be able to run the commands unlinked as in:

command; myscript

And have myscript retrieve $?, i.e. the exist status. I know I could run:

command; myscript $?

But what I'm really looking for is a Python way to retrieve $? from Python.

How can I do it?


Since this is a strange request, let me clarify where it comes from. I have a Python script that uses the pushover API to send a notification to my phone.

When I run a long process, I run it as process && notify "success" || notify "failure". But sometimes I forget to do this and just run the process. At this point, I'd like to run "notify" on the still processing command line, and have it pick up the exit status and notify me.

Of course I could also implement the pushover API call in bash, but now it's become a question of figuring out how to do it in Python.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Miquel
  • 15,405
  • 8
  • 54
  • 87
  • 1
    did you try to explicite 'run' `$?` via call() or something ? – 4rlekin Oct 29 '14 at 09:34
  • 1
    That is a really good question. I've been looking, and haven't found anything so far. You could do `command; SOMEVAR=$?; myscript` and then retrieve `os.environ["SOMEVAR"]` inside your script, but that's no better than doing `command; myscript $?`. I'll come back with an answer if I find anything. – rmunn Oct 29 '14 at 09:43
  • @4rlekin good idea, but it doesn't seem to work. `print subprocess.call("/bin/echo $?", shell=True)` always returns 0. I suspect it's because the command is executed in a new shell that doesn't know about exist status of its grandparent's shell (original bash runs python, runs bash) – Miquel Oct 29 '14 at 09:48
  • depending on what do you want to achieve with this script, the stupid-simple solution would be write script in bash already (and call all python-specific functionalities from within) – 4rlekin Oct 29 '14 at 09:51
  • After further testing, I think it can't be done. The reason why would be a bit too long to put in comments, so I'll give it as an answer. – rmunn Oct 29 '14 at 10:00
  • Just saw your update to clarify why you want this, and updated my answer with my best suggestion. – rmunn Oct 29 '14 at 12:44

4 Answers4

3

This may not be possible, because of how a script (of any language, not just Python) gets executed. Try this: create a shell script called retcode.sh with the following contents:

#!/bin/bash
echo $?

Make it executable, then try the following at a shell prompt:

foo # Or any other non-existent command
echo $?  # prints 127
foo
./retcode.sh  # prints 0

I'd have to double-check this, but it seems that all scripts, not just Python scripts, are run in a separate process that doesn't have access to the exit code of the previous command run by the "parent" shell process. Which may explain why Python doesn't (as far as I can tell) give you a way to retrieve the exit code of the previous command like bash's $? — because it would always be 0, no matter what.

I believe your approach of doing command; myscript $? is the best way to achieve what you're trying to do: let the Python script know about the exit code of the previous step.

Update: After seeing your clarification of what you're trying to do, I think your best bet will be to modify your notify script just a little, so that it can take an option like -s or --status (using argparse to make parsing options easier, of course) and send a message based on the status code ("success" if the code was 0, "failure NN" if it was anything else, where NN is the actual code). Then when you type command without your little && notify "success" || notify "failure" trick, while it's still running you can type notify -s $? and get the notification you're looking for, since that will be the next thing that your shell runs after command returns.

rmunn
  • 34,942
  • 10
  • 74
  • 105
  • Thanks. Yes, you are right, your first script is run in a bash of its own that knows nothing of the exit status of the parent bash. This would only work if the parent bash were to pass `$?` down to the children, which it doesn't. I was hoping python might pick up the value though. This would be possible but seems to not be done. It would be equivalent to: `command; python $? script` where `$?` is amde available inside script – Miquel Oct 29 '14 at 10:08
  • 1
    You can tell the shell to run your command in the current shell by using a dot in front. Such as. ' . ./foo '. Without the preceding dot it spawn a subshell to run the command – Doon Oct 29 '14 at 10:31
  • @Doon Good point, that certainly works in the case of bash scripts after each other. I cannot source (`.`) python though – Miquel Oct 29 '14 at 10:37
  • Thanks for the update, and for the patience to go into the details of what I wanted to do. I understand it's somewhat nitpicking :) I have now adopted your solution. Thanks! – Miquel Oct 29 '14 at 15:22
3
false; export ret=$?; ./myscript2.py

myscript2.py:

#!/usr/bin/python

import os
print os.environ['ret']

Output:

1
Cyrus
  • 84,225
  • 14
  • 89
  • 153
  • Thanks for your answer. I was trying to hint at this in the question though. While your solution would work, this has now degenerated into a very nitpicky argument on getting `$?` from python without any explicit parameter passing. Still, good solutions both! Thanks – Miquel Oct 29 '14 at 10:39
  • I've removed the first answer. – Cyrus Oct 29 '14 at 18:01
1

But what I'm really looking for is a Python way to retrieve $? from Python.

If you ran them as separate processes, then it's clearly impossible.

An independent process cannot know how another process ended.

You can 1) store the result in a file, 2) emit something from command, and pipe it to the script 3) run the command from myscript, and so on... but you need some kind of communication.

The simplest way, of course is command; myscript $?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
  • I understand the process separation, but I was looking for the slim chance that python picked up `$?` on its own and made it available to my scripy. Guess that's not the case. Thanks for clarifying! – Miquel Oct 29 '14 at 10:04
1

It is clearly not possible: the exit value of a process is only accessible to its parent, and no shells I know offer an API to allow next process to retrieve it.

IMHO, what is closer to your need would be:

process
myscript $?

That way, you can do it even if you started you process without thinking about notification.

You could also make the script able to run a process get the exit code and to its notification, or (depending on options given in command line) use an exit code given as parameter. For example, you could have:

  • myscript process: runs process and does notifications
  • myscript -c $?: only does notifications

argparse module can make that easy.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252