1. 22
  1.  

  2. 19
    [ $USER != "root" ] && echo You must be root && exit 1
    

    I’ve always felt a bit uneasy about this one. I mean, what if echo fails? :-)

    So I usually do

    [ $USER != "root" ] && { echo You must be root; exit 1; }
    

    instead… just to be safe.

    1. 10

      Indeed, echo can fail. Redirecting stdout to /dev/full is probably the easiest way to make this happen but a named pipe can be used if more control is required. The sentence from the article “The echo command always exists with 0” is untrue (in addition to containing a typo).

      1. 3

        Don’t you need set +e; before echo, just to be extra safe?

        1. 3

          I had to look that up. set +e disables the -e option:

                    -e      Exit immediately if a simple command (see SHELL  GRAMMAR
                            above) exits with a non-zero status
          

          That’s not enabled by default, though, and I personally don’t use it.

          1. 1

            Or &&true at the end, if it’s okay for this command to fail. EDIT: see replies

            It’s as much of a kludge as any other, and I’m not sure how to save the return value of a command here, but bash -ec 'false && true; echo $?' will return 0 and not exit from failure. EDIT: it echoes 1 (saving the return value), see replies for why.

            1. 2

              You probably mean || true. But yeah, that works!

              1. 1

                I did mean || true, but in the process of questioning what was going on I learned that && true appears to also prevent exit from -e and save the return value!

                E.G.,

                #!/bin/bash -e
                f(){
                return 3
                }
                f && true ; echo $?
                

                Echoes 3. I used a function and return to prove it isn’t simply a generic 1 from failure (as false would provide). Adding -x will also show you more of what’s going on.

          2. 2

            I personally use the following formatting, which flips the logic, uses a builtin, and printd to stderr.

            [ "${USER}" == "root" ] || {printf "%s\n" "User must be 'root'" 1>&2; exit 1; }

            When I start doing a larger amount of checks, I wrap the command group within a function, which turns into the following, and can optionally set the exit code.

            die() { printf "%s\n" "${1}" 1>&2; exit ${2:-1}; }
            ...
            [ "${USER}" == "root" ] || die "User must be 'root'"
            
            1. 2

              I also always print to standard out, but I’m pretty sure most shells have echo as a built-in. The form I usually use is

              err() { echo "$1" 1>&2; exit 1; }
              
          3. 12

            This article leaves aside the most useful special parameter for elegant conditional: the “most recent argument”, $_.

            Example of use: test -f /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh && source $_ || echo "zsh-syntax-highlighting not installed" >&2

            1. 6

              Even

              test -f /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh \
              && source "$_" \
              || echo "${_##*/} is not installed" >&2
              

              😎

              1. 3

                TIL. Thank you for sharing, I never knew that one.

                1. 2

                  Brilliant! Wasn’t aware. Added it in the post.

                2. 8

                  Nothing elegant about this, just less verbose, and IMHO, less readable. Either future you after six months or someone willing to contribute to your project who is not that familiar with sh, and all to save some lines?

                  For me this is on the same line as -s vs –short flags. Long flags are for scripts, short flags are for the cli. As if I myself would glance over a script and know what wget -k does instead of wget –insecure (example).

                  1. 2

                    I believe this turn to be quite readable instead. Take a look at this:

                    init &&
                      configure &&
                      install &&
                      cleanup ||
                      echo Install failed
                    

                    Every line state clearly what’s going on. This is quite functional style alike. Removing the verbosity of the if statement gives succinctness and clarity to the code.

                    Personally to increase clarity I do use a basic convention for variables and methods :

                    Prefixes 
                    is | has := return or contains a bool
                    do := perform an action
                    on := trigger on event
                    
                    Eg:
                    isValidTag() 
                        && isNotEmptyTag() 
                        && hasLessThan5Tags() 
                        && doAppendTag()
                    
                    1. 2

                      I think the if statement style is at least as easy to read if not easier:

                      if ! (init && configure && install && cleanup); then
                          echo Install failed
                      fi
                      
                  2. 7
                    test -f FILE && source $_ || echo "FILE does not exist" >&2
                    

                    This doesn’t work if the filename has a space or an asterisk in it. You need to surround $_ with double-quotes. https://www.shellcheck.net/ is your friend.

                    1. 1

                      assuming it was added based on @Diti ‘s comment above, it would work for the intended shell, zsh since zsh doesn’t expand unquoted variables the same. I agree the author should probably update this.

                    2. 5

                      Anecdotally, I have polled the developers where I work whether they prefer the slick && conditional or the straightforward verbose if statement. The results overwhelmingly favor the if statement. These are devs experienced with bash, so this doesn’t even consider that:

                      1. It may confuse devs less familiar with bash.
                      2. The “echo may fail problem” raised by johnaj.
                      1. 2

                        All of these also seem to work in POSIX sh, just FYI.