1

I have the following in a python script (using python 3.4), if there is any failure:

exit(1)

The purpose of this python script is to print() some value, so that it may be assigned to a shell variable like so:

#!/bin/bash
set -e
result=$(python my-script.py)
echo $?

However, if the script fails, the echo above returns 0. I want set -e to immediately interrupt and fail the script if the python script fails (i.e. returns non zero exit code).

How can I make this work? I tried set -eo pipefail as well in my bash script and I've noticed no difference.

I'm running Ubuntu 15.

EDIT

My python script (verbatim) just in case it is the suspect here...

import re
import sys

version_regex = r'(?:(?:release|hotfix)\/|^)([\d.]+)-\d+-g[a-f\d]+$'

result = re.search(version_regex, sys.argv[1])
if not result:
    exit(1)

print(result.group(1))

Note that I have tried both sys.exit(1) and exit(1) in my experiments but I never saw my bash script fail.

EDIT 2

parsed_version=$(python parse-git-describe.py $describe_result; echo $?)
echo $parsed_version
echo $?

The above bash script gives me the following output:

1
0

Note that the script parse-git-describe.py is the same as the python script provided earlier.

EDIT 3

Apparently local causes this to break. EDIT 2 above was wrong, that is the result with local inside a shell function:

foo()
{
    local parsed_version=$(python parse-git-describe.py $describe_result; echo $?)
    echo $parsed_version
    echo $?
}

Result is:

1
0

But if I remove local it works fine?

foo()
{
    parsed_version=$(python parse-git-describe.py $describe_result; echo $?)
    echo $parsed_version
    echo $?
}

Result is:

1
1

Why does local matter?

Cœur
  • 37,241
  • 25
  • 195
  • 267
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • 1
    See [this](https://stackoverflow.com/questions/19622198/what-does-set-e-mean-in-a-bash-script) SO post. Mentions `set -e` as unreliable and bad practice. Your script may not necessarily reach the `echo` – code_dredd Oct 14 '15 at 18:07
  • Since you're using `exit` directly, try changing your import to `from sys import exit`. – code_dredd Oct 14 '15 at 18:16
  • I already read that post, and I know people suggest `trap`, but I'm not sure how to use it in my specific case. – void.pointer Oct 14 '15 at 18:17
  • @ray my understanding of `sys.exit()` in python is that it is intended for "applications", whereas `exit()` is for interactive shell (which is my case) – void.pointer Oct 14 '15 at 18:18
  • Your script terminates with `local: can only be used in a function` for me. – tripleee Oct 14 '15 at 18:18
  • I removed `local`, it was a bad copy/paste from my script. – void.pointer Oct 14 '15 at 18:20
  • @void.pointer: Based on your shell script example, you don't appear to be using the interactive *python* shell. – code_dredd Oct 14 '15 at 18:27
  • @ray Can you clarify? I do not know what you mean. – void.pointer Oct 14 '15 at 18:29
  • @void.pointer: `$ python -c 'from sys import exit; exit(1)'; echo $?` works fine for me, printing 1. Consider a simple test with simpler scripts to *reproduce* and isolate the issue. (Also on Ubuntu 15, BTW.) Did you check the post I linked to regarding `set -e`? Consider getting the return code and performing an if-check in your bash script and exit directly instead of relying on `set -e`. – code_dredd Oct 14 '15 at 18:43
  • Made a 3rd edit. Seems that `local` causes different behavior, I don't understand that at all. – void.pointer Oct 14 '15 at 18:52
  • @void.pointer: See my post regarding your question on `local`. – code_dredd Oct 14 '15 at 20:44
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/92316/discussion-between-ray-and-void-pointer). – code_dredd Oct 14 '15 at 21:10

3 Answers3

1

You can to set the exit code if you finish your script by calling sys.exit(code).

aganders3
  • 5,838
  • 26
  • 30
  • I do this already though... I will update my post with the python script verbatim – void.pointer Oct 14 '15 at 18:11
  • You're using the built-in `exit`. Change your `exit(1)` to `sys.exit(1)` or do as @ray mentioned and add `from sys import exit` to the top of your script. – aganders3 Oct 14 '15 at 18:19
  • Why does it matter? I've read that both exit calls eventually call the same thing anyway. I tried them both in my experiments and I have not noticed a difference. – void.pointer Oct 14 '15 at 18:30
1

The local can only be used within a function in bash. (So your bash script code above doesn't work either.)

Why don't you call your python script in the bash script and check the return value and then jump out of your script if necessary?

$ python -c "exit(0)"
$ [ $? == 0 ] || echo "fail"
$ python -c "exit(1)"
$ [ $? == 0 ] || echo "fail"
fail

You can easily adapt this in your shell script, I guess.

ferdy
  • 7,366
  • 3
  • 35
  • 46
  • Actually my bash code was a copy & paste from a function, I pieced it together for my question here. Small oversight, I will correct it. I'm not actually using `local` outside of a function in my real script thought. – void.pointer Oct 14 '15 at 18:19
  • 1
    A much better and more idiomatic approach would be `python -c 'exit(1)' || echo "fail"` – tripleee Oct 14 '15 at 18:22
  • I'd recommend to pass on the error codes to the calling function also in your bash script and then slip out of the script according to it. – ferdy Oct 14 '15 at 18:22
  • @tripleee I just wanted to make it more visible for the questioner how to pass a return value to the calling bash script... – ferdy Oct 14 '15 at 18:24
  • @ferdy My bash script as I wrote it in my question is essentially testing for the same thing. My echo call returns 0, not 1. – void.pointer Oct 14 '15 at 18:32
0

Why does local matter??

The local keyword specifies the variable's scope. The following sample script should illustrate:

#!/bin/bash
HELLO=Hello    # global variable, HELLO

function hello {
    local HELLO=World   # local variable with same name; diff scope
    echo $HELLO         # this is the local, not global!
}

echo $HELLO  # print global
hello        # function call!
echo $HELLO  # global unchanged!

Running this code from the shell (placed in test.sh script) produces:

➜  Projects  ./test.sh
Hello
World
Hello

When you use local, the variable and the exit code it had go out of scope when the function returns to the caller. Your function should return the value to the caller if you don't want that to happen.

code_dredd
  • 5,915
  • 1
  • 25
  • 53
  • Again you're still missing my point. See the example I put here. Notice that the `local` keyword changes the behavior of being able to detect exit code. http://goo.gl/1J4IB1 – void.pointer Oct 15 '15 at 00:48