0

This works as expected:

#!/bin/bash
set -ex
V="$(cat non-existant-file)"
echo "var V: $V"

the script fails on line 3 and exits without executing line 4 thanks to the "set -e".

Now, small change:

#!/bin/bash
set -ex
readonly V="$(cat non-existant-file)"
echo "var V: $V"

And now the script executes line 4. Same behavior when running the script in sh.

bruce_ricard
  • 743
  • 1
  • 7
  • 15
  • Can't reproduce the issue with `GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)`. – Petr Skocik Aug 17 '18 at 21:01
  • 2
    `set -e` is a not a reliable way of handling errors. See [BashFAQ/105](https://mywiki.wooledge.org/BashFAQ/105). See [Raise error in a Bash script](https://stackoverflow.com/q/30078281/6862601) for more details about proper error handling. – codeforester Aug 17 '18 at 21:07
  • 2
    It's `#!`, not `!#`. – chepner Aug 17 '18 at 21:47
  • ...closely related: [why does `local` sweep the return code of a command?](https://stackoverflow.com/questions/4421257/why-does-local-sweep-the-return-code-of-a-command). I could have sworn we already had a duplicate for `readonly`, but it's not being trivial to find. (Update: Found it!) – Charles Duffy Aug 17 '18 at 22:12

1 Answers1

3

This is because the line readonly V="$(cat non-existant-file)" is not a simple assignment: it is the composition of an assignment that fails, followed by the instruction readonly V, which succeeds.

That explains the behavior you observed, and this Bash pitfall is mentioned for a similar construct (local) in the documentation BashFAQ/105 indicated by @codeforester.

So, if you try instead the following code, you should observe the behavior you expect:

#!/bin/bash
set -ex
V=$(cat non-existant-file)
readonly V
echo "var V: $V"

Minor remarks:

  • I corrected the shebang that should be #!/usr/bin/env bash or #!/bin/bash, not !#/bin/bash

  • I replaced V="$(cat non-existant-file)" with V=$(cat non-existant-file) because the quotes are unnecessary here.

ErikMD
  • 13,377
  • 3
  • 35
  • 71
  • I though it might be that. But I would expect the whole line to fail if either the assignment or the readonly instruction fails. Thanks! – bruce_ricard Aug 17 '18 at 21:54
  • 2
    @bruce_ricard indeed this fact is not obvious, so actually the thing to remember is: never write `local V=$(command)` nor `readonly V=$(command)` in presence of `set -e`, but rather split the compound instruction in 2 parts: `local V; V=$(command)` or respectively `V=$(command); readonly V`. – ErikMD Aug 17 '18 at 22:03