1. 25
  1. 4

    Today I learned about the “if myfunc” pitfall. I’m pretty fond of shell programming in general, in a punk/lomography kind of way, but that one scares me.

    1. 2

      Yeah it comes up on the bash mailing list with some frequency. I think it’s been true for 30 years or more, and every so often there is a big thread about it. Hence “groundhog day”.

      Here’s another one (where I participate but so do many other people):

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

      It’s weird shell folk knowledge.

      I add this to my scripts to flag it when run under OSH:

      shopt -s strict:all 2>/dev/null || true 
      

      https://github.com/oilshell/oil/blob/master/test/spec.sh#L9

      (Right now Oil’s own scripts keep are run under bash as well.)

      1. 1

        +1 for

        punk/lomography

        I’ll henceforth use that to explain the essence of shell programming.

      2. 3

        # try sets _status, not $?

        why is that?

        1. 8

          Good question, I will put this in the FAQ !

          Basically the idea is that idiomatic Oil scripts never look at $? – it’s only used to trigger the errexit rule.

          Instead they invoke try and inspect _status when they want to handle errors


          Some detail: try is a builtin that takes a block, and every builtin has an exit code.

          The exit status of try is always zero, because if it returned a non-zero status for $?, then the errexit rule would trigger, and you wouldn’t be able to handle the error!

          So there are no special cases in the interpreter for try.

          For context, other builtins that take a Ruby-like block:

          • cd – save and restore current dir
          • shopt – save and restore option state
          • forkwait – “replacement” for subshell ( cd / ; echo $PWD ) which is rarely used in Oil
          • fork – replacement for &

          Generally the errors occur INSIDE the block, not outside, like:

          cd /tmp {
             cp zzzz /   # error happens here
             echo done 
          }  # not here
          

          Of course you can nest cd and try if you want.

          And the _ convention for _status is for globals / “registers” that the shell itself mutates:

          • _status, and the rarely used _pipeline_status, _process_sub_status
          • _match(1) for the match of a regular expression
          • _this_dir for imports relative to the current directory

          Related questions: https://www.oilshell.org/release/0.10.0/doc/error-handling.html#faq-on-language-design (why is there try but no catch)

          1. 2

            Good answer :)

            That’s pretty neat, being able to implement this without special cases.

            1. 1

              Glad it made sense! A related thing that I think will prove useful is that you don’t have to pass a block literal to try. Instead you can pass a variable like

              const myblock = &(cd /tmp; echo $PWD)
              try (myblock)   # typed argument to try
              

              I mentioned this in a thread a few months ago:

              https://lobste.rs/s/irvnko/recent_progress_on_oil_shell

              I don’t expect this to be common in app code, but I can see it being useful in test frameworks and the like.