Bash – Understand why a bash script is not executing main

bashgrepshell-script

I performed a clone of Zcash:

$ git clone https://github.com/zcash/zcash.git
$ cd zcash/
$ git checkout v1.0.1
$ ./zcutil/fetch-params.sh

I'm trying to run fetch-params.sh on both OS X and Solaris. Its a Linux script, so it needs a little messaging. Below is the immediate diff; and the complete script with changes is at the end of the question.

diff --git a/zcutil/fetch-params.sh b/zcutil/fetch-params.sh
index ac5327b..e2e9807 100755
--- a/zcutil/fetch-params.sh
+++ b/zcutil/fetch-params.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash

 set -eu

@@ -12,6 +12,9 @@ SPROUT_VKEY_URL="https://z.cash/downloads/$SPROUT_VKEY_NAME"
 SHA256CMD="$(command -v sha256sum || echo shasum)"
 SHA256ARGS="$(command -v sha256sum >/dev/null || echo '-a 256')"

+IS_DARWIN=$(uname -s | grep -i -c darwin)
+IS_SOLARIS=$(uname -s | grep -i -c sunos)
+
 function fetch_params {
     local url="$1"
     local output="$2"
@@ -45,13 +48,20 @@ EOF

 # Use flock to prevent parallel execution.
 function lock() {
-    local lockfile=/tmp/fetch_params.lock
-    # create lock file
-    eval "exec 200>/$lockfile"
-    # acquire the lock
-    flock -n 200 \
-        && return 0 \
-        || return 1
+   local lockfile="/tmp/fetch_params.lock"
+    if [[ ("$IS_SOLARIS" -ne "0" || "$IS_DARWIN" -ne "0") ]]; then
+        # http://unix.stackexchange.com/a/13025
+        mkdir "$lockfile" \
+            && return 0 \
+            || return 1
+    else
+        # create lock file
+        eval "exec 200>/$lockfile"
+        # acquire the lock
+        flock -n 200 \
+           && return 0 \
+           || return 1
+    fi
 }

 function exit_locked_error {
@@ -105,5 +115,11 @@ EOF
 }

 main
-rm -f /tmp/fetch_params.lock
+
+if [[ ("$IS_SOLARIS" -ne "0" || "$IS_DARWIN" -ne "0") ]]; then
+    rm -rf /tmp/fetch_params.lock
+else
+    rm -f /tmp/fetch_params.lock
+fi
+
 exit 0

The problem I am having is main is not executing. The script quietly exits after the IS_SOLARIS=$(uname -s | grep -i -c sunos):

riemann:zcash$  bash -x ./zcutil/fetch-params.sh 
+ set -eu
...
++ uname -s
++ grep -i -c darwin
+ IS_DARWIN=1
++ uname -s
++ grep -i -c sunos
+ IS_SOLARIS=0
riemann:zcash$ 

Here's the weird thing… It happens on both OS X and Solaris. And if I set change IS_SOLARIS=$(uname -s | grep -i -c sunos) to IS_SOLARIS=0 on OS X, then it works. Or if I comment out set -eu then it works. I've never used set -eu before, but I don't understand its [possible] failure given the explanation at When to use set -e.

OS X provides GNU bash, version 3.2.53; while Solaris 11 provides GNU bash, version 4.1.17.

Please forgive my ignorance… Why does the test for IS_DARWIN and IS_SOLARIS cause the script to die? Why is it not executing main?


Full Modified Script

#!/usr/bin/env bash

set -eu

PARAMS_DIR="$HOME/.zcash-params"

SPROUT_PKEY_NAME='sprout-proving.key'
SPROUT_VKEY_NAME='sprout-verifying.key'
SPROUT_PKEY_URL="https://z.cash/downloads/$SPROUT_PKEY_NAME"
SPROUT_VKEY_URL="https://z.cash/downloads/$SPROUT_VKEY_NAME"

SHA256CMD="$(command -v sha256sum || echo shasum)"
SHA256ARGS="$(command -v sha256sum >/dev/null || echo '-a 256')"

IS_DARWIN=$(uname -s | grep -i -c darwin)
IS_SOLARIS=$(uname -s | grep -i -c sunos)

function fetch_params {
    local url="$1"
    local output="$2"
    local dlname="${output}.dl"
    local expectedhash="$3"

    if ! [ -f "$output" ]
    then
        echo "Retrieving: $url"
        wget \
            --progress=dot:giga \
            --output-document="$dlname" \
            --continue \
            --retry-connrefused --waitretry=3 --timeout=30 \
            "$url"

        "$SHA256CMD" $SHA256ARGS --check <<EOF
$expectedhash  $dlname
EOF

        # Check the exit code of the shasum command:
        CHECKSUM_RESULT=$?
        if [ $CHECKSUM_RESULT -eq 0 ]; then
            mv -v "$dlname" "$output"
        else
           echo "Failed to verify parameter checksums!"
           exit 1
        fi
    fi
}

# Use flock to prevent parallel execution.
function lock() {
    local lockfile="/tmp/fetch_params.lock"
    if [[ ("$IS_SOLARIS" -ne "0" || "$IS_DARWIN" -ne "0") ]]; then
        # http://unix.stackexchange.com/a/13025
        mkdir "$lockfile" \
            && return 0 \
            || return 1
    else
        # create lock file
        eval "exec 200>/$lockfile"
        # acquire the lock
        flock -n 200 \
           && return 0 \
           || return 1
    fi
}

function exit_locked_error {
    echo "Only one instance of fetch-params.sh can be run at a time." >&2
    exit 1
}

function main() {

    lock fetch-params.sh \
    || exit_locked_error

    cat <<EOF
Zcash - fetch-params.sh

This script will fetch the Zcash zkSNARK parameters and verify their
integrity with sha256sum.

If they already exist locally, it will exit now and do nothing else.
EOF

    # Now create PARAMS_DIR and insert a README if necessary:
    if ! [ -d "$PARAMS_DIR" ]
    then
        mkdir -p "$PARAMS_DIR"
        README_PATH="$PARAMS_DIR/README"
        cat >> "$README_PATH" <<EOF
This directory stores common Zcash zkSNARK parameters. Note that it is
distinct from the daemon's -datadir argument because the parameters are
large and may be shared across multiple distinct -datadir's such as when
setting up test networks.
EOF

        # This may be the first time the user's run this script, so give
        # them some info, especially about bandwidth usage:
        cat <<EOF
The parameters are currently just under 911MB in size, so plan accordingly
for your bandwidth constraints. If the files are already present and
have the correct sha256sum, no networking is used.

Creating params directory. For details about this directory, see:
$README_PATH

EOF
    fi

    cd "$PARAMS_DIR"

    fetch_params "$SPROUT_PKEY_URL" "$PARAMS_DIR/$SPROUT_PKEY_NAME" "8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7"
    fetch_params "$SPROUT_VKEY_URL" "$PARAMS_DIR/$SPROUT_VKEY_NAME" "4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82"
}

main

if [[ ("$IS_SOLARIS" -ne "0" || "$IS_DARWIN" -ne "0") ]]; then
    rm -rf /tmp/fetch_params.lock
else
    rm -f /tmp/fetch_params.lock
fi

exit 0

Best Answer

You can just use the Bash pattern matching like this.

[[ $(uname) = *[Dd]arwin* ]]; IS_DARWIN=$?
[[ $(uname) = *[Ss]un[Oo][Ss]* ]];  IS_SOLARIS=$?

This is an extended test builtin to Bash. To cut down on the calls to uname save the value to a variable.

uname=$(uname)
[[ $uname = *[Dd]arwin* ]]; IS_DARWIN=$?
[[ $uname = *[Ss]un[Oo][Ss]* ]];  IS_SOLARIS=$?

This can be further simplified as:

uname=$(uname)
[[ $uname =~ [Dd]arwin ]]; IS_DARWIN=$?
[[ $uname =~ [Ss]un[Oo][Ss] ]];  IS_SOLARIS=$?

Or as:

uname=$(uname | tr [A-Z] [a-z])
[[ $uname =~ darwin ]]; IS_DARWIN=$?
[[ $uname =~ sunos ]];  IS_SOLARIS=$?

Or simplest

uname=$(uname)
[[ $uname =~ Darwin ]]; IS_DARWIN=$?
[[ $uname =~ SunOS ]];  IS_SOLARIS=$?

Example runs from Solaris (SunOS).

-bash-3.2$ uname
SunOS
-bash-3.2$ uname=$(uname | tr [A-Z] [a-z])
-bash-3.2$ [[ $uname =~ sunos ]];  IS_SOLARIS=$?; echo $IS_SOLARIS
0

-bash-3.2$ uname
SunOS
-bash-3.2$ uname=$(uname)
-bash-3.2$ [[ $uname =~ [Ss]un[Oo][Ss] ]];  IS_SOLARIS=$?; echo $IS_SOLARIS
0

-bash-3.2$ uname
SunOS
-bash-3.2$ uname=$(uname)
-bash-3.2$ [[ $uname =~ SunOS ]];  IS_SOLARIS=$?; echo $IS_SOLARIS
0
-bash-3.2$

-bash-3.2$ uname
SunOS
-bash-3.2$ [[ $(uname) =~ SunOS ]];  IS_SOLARIS=$?; echo $IS_SOLARIS
0
-bash-3.2$
Related Question