1. 25
  1. 24

    The author is against -euo pipefail, and the reasons are totally unconvincing. I can’t take it seriously. Being against -euo pipefail is like being against seatbelts because you should just not crash.

    1. 9

      Threading the needle, here…

      1. I agree the reasons in the post are a little thin.
      2. But I also think the seatbelt statement goes a step too far? It’s not a zero-consequence safety mechanism… https://mywiki.wooledge.org/BashPitfalls#set_-euo_pipefail (and by proxy https://mywiki.wooledge.org/BashFAQ/105, https://mywiki.wooledge.org/BashFAQ/112).
      1. 5

        Erm, no. set -euxo pipefail may be useful for very small projects, but blindly adding these options may actually break your scripts.

        Yeah, that’s an odd section. I’m hesitant to read more after jumping to that—I think there’s some fundamental misunderstandings there.

        set -x doesn’t really matter, that’s not really core to the utility of the common “Bash safe mode.” I’d imagine it’s not even included in most places that suggest that.([1], [2], two random articles I googled) Add it or don’t add it, or enable it on a case-by-case basis when running the script, who cares.

        set -u is “limiting” in that it forces you to know whether or not you expect a variable to be set. You can test for the presence of a variable by setting an empty default and that, to me, seems like a good thing.

        With this as script.bash:

        if [[ -z "${foo:-}" ]]
        then
            printf '%s\n' "not there"
        else
            printf '%s\n' "there"
        fi
        

        You can get this:

        $ bash script.bash 
        script.bash: line 4: foo: unbound variable
        $ bash script.bash 
        not there
        script end
        $ foo=bar !!
        foo=bar bash script.bash 
        there
        script end
        

        The first run has "${foo}" and the second run has "${foo:-}".

        set -e is more complicated, admittedly, but I think there are ways to return non-zero status in certain constructs.

        1. 5

          As the author: I find it a bit sad, that people comment only on, what I find the least interesting chapter at the end of the booklet. Given the feedback l’ll probably rewrite the text for the -u flag somewhat.

          I didn’t know that you could circumvent -u with ${foo:-} for the -z test. I don’t see how this would work for the -v test though.

          #! /usr/bin/env bash
          
          
          set -u
          
          unset a
          x=a
          
          if [ -v "${!x}" ]  
          then
             echo "set"
          else
             echo "unset"
          fi
          

          Now here you get “set”, which is not really what I want. I am not dogmatic on this, if there is a way to do this with -u I might change my ways.

          1. 3

            Is there a reason you use ${!x} indirect parameter expansions, rather than namerefs? If you use namerefs then -v executes as you would expect.

            set -u
            
            unset a
            declare -n x=a
            
            if [ -v x ]; then
            	echo "set"
            else
            	echo "unset"
            fi
            
            1. 3

              As the author: I find it a bit sad, that people comment only on, what I find the least interesting chapter at the end of the booklet.

              Dan Abramov wrote about this phenomenon:

              Your project hit the front page of a popular news aggregator. Somebody visible in the community tweeted about it too. What are they saying?

              Your heart sinks.

              It’s not that people didn’t like the project. You know it has tradeoffs and expected people to talk about them. But that’s not what happened.

              Instead, the comments are largely irrelevant to your idea.

              The top comment thread picks on the coding style in a README example. It turns into an argument about indentation with over a hundred replies and a brief history of how different programming languages approached formatting. There are obligatory mentions of gofmt and Python. Have you tried Prettier?

              This basically always happens. As a blog author, I’ve been on the other end of it a couple of times. I think it’s just an inevitable result of how social sites work. People generally look at the comments separately from the articles, so if someone posts a random comment early in the process, it tends to drive the whole conversation in some direction that is irrelevant to 99% of what you wrote. It kind of sucks, but it is what it is.

              1. 1

                Is using -v over -z in this particular context necessary? I get that it’s “the” test for that, but -z works and allows you have the rest of the script run with nounset.

                1. 2

                  If you need to know if a variable has been set (possibly from the environment) or not regardless of the contents, then AFAIK the -v test is the way to go. You could argue, that you’d turn -u off and back on in this instance. And maybe I would be tempted to agree… But I am not sure yet, as in my experiences -u was more obstacle then help.

              2. 2

                What’s tricky about -e? If you want a line to continue even if the return value is bad, add || true to the end and it works. It defaults to failing (which is what you want unless you like zombie scripts), but you can override it if you need to. I can’t imagine writing correct Bash without -e.

                1. 4

                  Someone else already linked, but I think https://mywiki.wooledge.org/BashFAQ/105 is a pretty good argument. Worth reading if nothing else for the “Story Time” metaphor which applies far beyond bash.

                  Fwiw, I still use it, because the convoluted “bunch of special rules” actually mostly do what you want, so despite the negatives I think it’s usually a net benefit.

                  1. 1

                    I agree it is useful, I am more conceding that it is more complicated. @abathur shared a good link with examples in this thread, it does get a bit tangly when the command get abstracted away.

                2. 3

                  I think dismissing the whole article is a mistake. This is only one small section, and other respected authorities agree with the author (which I’m not saying ends the discussion – but suggests that the position is not laughable).

                  I’ve written a decent amount of bash and read many articles on it, and I while I didn’t agree with everything here (eg, the macro recommendations, while clever, are a bit too clever for my taste and I would avoid them outside of personal scripts), I think there’s good content, and some nuggets that were new to me.

                  I’d recommend it.

                  1. 4

                    Thanks for saving me a click. That paints the author in a very specific light, and I agree is a non-starter.

                  2. 9

                    Modern Bash Scripting is implementing it instead in Go/Python/<insert language you like> for anything longer than 4 lines.

                    1. 2

                      I can shamelessly recommend Next Generation Shell which was designed to be exactly that language.

                      https://github.com/ngs-lang/ngs

                    2. 7

                      Writing shell scripts used to be a major, major pain for me.

                      Same for me. This is why I created https://github.com/google/zx

                      1. 3

                        ShellCheck makes the previously tedious search for that elusive missing backtick or doublequote super easy, barely an inconvenience:

                        - later in the series -

                        I also can’t really let go of backticks

                        I can’t help but think that switching from “``” to “$()” might help with string matching tasks…

                        Also, kinda confused why traps weren’t mentioned at all. Yes, using “-euxo pipefail” can sometimes make code fail unexpectedly, but combining them with traps makes scripts considerably more reliable and allows scripts to cleanup after themselves.

                        A rather silly example that tends to fail in predictable ways: https://github.com/cmonr/gh/blob/master/gh#L179

                        1. 2

                          Also, kinda confused why traps weren’t mentioned at all. Yes, using “-euxo pipefail” can sometimes make code fail unexpectedly, but combining them with traps makes scripts considerably more reliable and allows scripts to cleanup after themselves.

                          For one it’s a work in progress, so I could still write something about traps. But more importantly, I don’t know what to write about traps, that hasn’t been covered in other bash guides already. Whereas the techniques I cover like for instance the .for .do .done macros or the zsh/bash starter are new or like the subshell function at least unusual enough to warrant mentioning. That not a single comment here has picked up on the new stuff, but that the discussion circles exclusively around the familiar -euo pipefail trope, is probably telling.

                          1. 1

                            For one it’s a work in progress,

                            Oh, interesting. That’s actually not the impression that I picked up on, and could be part of the reason it’s been picking up a lot of heat.

                            Whereas the techniques I cover like for instance the .for .do .done macros or the zsh/bash starter are new or like the subshell function at least unusual enough to warrant mentioning.

                            That’s fair, though I do think that trap usage is still uncommon/unusual enough to warrent comment, but that’s also my opinion. Even outside of the ‘set’ discussion/heat, I always think that being able to cleanly fail is always desirable, but again, my opinion.

                            That not a single comment here has picked up on the new stuff

                            I think part of the problem might be that the list of pages was introduced as “Modern Bash Scripting”. I have a feeling that “Modern” is so overloaded, that different people entered the post with differing expectations, kinda like what would happen if someone read a list of suggestions starting with “Best, or “Minimal” or “Clean”.

                            If it was instead introduced as particularities, discoveries, or hacks, then maybe it would’ve gotten a better receiption?

                            1. 2

                              Some people are also ~triggered by Shell and are eager to dump on anything they perceive as promoting it.

                        2. 2

                          What do you think of Oil shell and the associated language?

                          1. 1

                            Good stuff overall. The use of alias is creative, but as you admit, it can be pretty perilous. For the log case, I don’t see how it is better (if not worse) than the log function with a branch. The pitfall mentioned is probably better mitigated by simply removing the comment altogether. : or true won’t use the arguments, but I suppose expansions and their side effects may cause issues.

                            If we’re talking about bash only, the looping over words, etc stuff could probably just simply be IFS=BLAH read -ra array_of_vals <<< "$input_here" and then you don’t need to worry about disabling globbing or abusing aliases.

                            For multi-return, why not RVAL=(v1 v2 v3) etc? Or for posixy shell, RVAL_1=…. RVAL_N=etc? Seems more generalized to me than needing to use quirky dynamic scoping locals or suffer the wrath of function specific global pollution.

                            Since you say this is a work in progress, I think it would help a lot to show how to do basic string handling. People underestimate a lot of the expansions zsh, bash and even posix standardized sh can do.

                            Also, have you heard of shopt -s lastpipe ? It is the default behavior of zsh and would remove the process substitution grossness <(cat blah).