1. 13
    1. 2

      OP here. About the workaround for the missing pipefail, I’d like to ask Lobsters for their opinion: do you see any drawback with this approach?

      1. 2

        I can see the following:

        • If you run the script from a shell, this shell will print “Terminated” at the end. At least it does for me, using bash. This will depend on your shell. This might be unexpected to the person running your program.
        • After the script exits, the other parts of the pipeline may still be running in the background. To see what I mean, change your sed -e s/3/5/ into something like perl -e "sleep 2; print <>". Run the script and it will print “Terminated” and return you to your prompt. Then 2 seconds later the output shows up on your terminal. You may be able to work around this and the previous point by sending SIGINT instead of SIGTERM.
        • The value of $? in the shell that launched the script is going to be strange (130, 143, etc). The actual exit code from the failing subprocess is lost.
        • A program can use WIFEXITED and WIFSIGNALED to determine whether a subprocess exited cleanly or was killed by a signal. Your script will show up as having been killed by a signal (because it was), not merely exiting with a non-zero exit status. If the program treats these two situations differently then you’ll end up with the wrong behavior.

        See also https://groups.google.com/forum/#!topic/comp.unix.shell/UHX-bBndq7k which is a scary looking monster.

    2. 2
      die { kill $self; }
      

      That is not valid shell code. I think you mean this?

      die() { kill $self; }
      

      Further, it would be better to avoid the global state when possible:

      die() { kill $1; }
      

      Also, unquoted variables should only be used when you explicitly want to invoke word splitting, something like this:

      s1=one,two,three
      IFS=,
      set $s1
      echo "$2"
      
      1. 1

        I think you mean this?

        Yes I do, that’s a typo (fix’d)

        avoid the global state

        Not feasible: when I invoke die I would need to pass the parent shell’s pid. The thing works under the assumption that $self contains the pid of the parent shell.

        set $s1

        Ok, I didn’t know about this! How does it work? At any rate, in this case I’m positive that $self is just going to be a number, even though it could be a style improvement to always quote it anyway.

        1. 3

          I’m positive that $self is just going to be a number

          Doesnt matter:

          https://unix.stackexchange.com/questions/171346/-/280935

          1. 2

            Holy cow, I would have never thought of that.

            You have to admit it’s quite sick, while I do have to admit it’s definitely worth a couple of quotes marks, to role that out! :-D

        2. 2
                 The third use of the set command is to set the values of the
                 shell's positional parameters to the specified args.  To change
                 the positional parameters without changing any options, use “--”
                 as the first argument to set.  If no args are present, the set
                 command will clear all the positional parameters (equivalent to
                 executing “shift $#”.)
          

          (from the dash(1) manpage) Now I know about it.

          Still I don’t see why I should change the parameters at runtime, unless my name is getopts or something.

    3. 2

      I’ve addressed the errexit problem in Oil, with a new mode

      shopt -s strict_errexit
      

      which makes it a programming error to use a function in a context where errexit is disabled. It may get tweaked a bit more.


      I’m going to write a big doc about it, but here are some relevant links:

      https://news.ycombinator.com/item?id=20786469

      https://lists.gnu.org/archive/html/bug-bash/2012-12/msg00093.html

      https://oilshell.zulipchat.com/#narrow/stream/121540-oil-discuss/topic/Overhauled.20errexit.20behavior

      On workaround for it is to use the “argv splice” pattern:

      set -e
      myfunc() ...
      
      
      if $0 myfunc; then
        this works
      fi
      
      "$@"
      
      
      1. 1

        Another message that explains the $0 / “argv splice” pattern;

        https://lists.gnu.org/archive/html/help-bash/2019-07/msg00002.html

    4. 1
      find(1)

      The -exec ... {} + semantics of find is to exec(2) the command with all arguments, so you get the exit code from the exec-ed command as a consequence (not a decision), while -exec ... {} \; keeps find in background until it exits with success (find did not encounter any error).

      pipe failure

      About this, all childs from the shell are immediate childs to sh (at least in mksh, ash, dash). So the shell could very much return failure on failure of any child rather than all childs!

      set -e

      This is a flag, using it is asking for this behavior. I see no quirk here.

      conclusion

      I see a mix of quirks and not-so quirks, but in all cases, the behavior of the shell is not helping a lot. Things could be simplified, others could be more helpful / intuitive (for reading the spec not reveal you did shoot your foot all this time, never a good surprise). If we change a standard, “everybody” complains. If we keep the itchy parts of a standard, everybody aches.

      We end-up letting the shell decay as a whole for the quirks engraved into it forever.

      Standard are meant to evolve! How often do you use FTP’s EBEDIC’s conversion features? It is still part of the standard.

      1. 2

        find(1)

        Thanks, it is great to think of it in terms of exec(2). And while it is true that find(1) itself doesn’t encounter errors in its internals, it is still impossible to do proper error detection, and I think it is a severe defect.

        pipe failure

        Yep, that would do. I honestly see no point why it should not be as you say, but we just keep it as it is.

        conclusion

        I don’t know about EBEDIC at all, but if it sucks we can simply not use it, I guess. On the other hand sh is ubiquitous, and definitely convenient, but you can’t disable those quirks :-/

        Oh well…

        At least it’s good to know about them :)

        1. 1

          Yes! Knowing shell quirks before we shoot ourself in the foot while dropping the gun.

          ls $file ? no I need to quote it. ls “$file” ? Oops what is file is -part-1.txt ? ls – “$file” ? Of course all shell scripts we encounters do that don’t they (I always forget about –) ?

          Also Yes! for shell quirks being enabled and unavoidable (as long as you care about standards).