galih
galih

Reputation: 511

bash - returning value based on process condition

i stumbled in a confusing way of conditionally returning value based on variable. I would like to check if process is successful then echo "process success", but if it's failed, i want to check specific error message then return the error message,

ERRMSG="$(cd /nonexist 2>&1)"
if [ $? -ne 0 ]
then
    if [ -z "$ERRMSG|grep -o 'No such file or directory'|head -1" ]
    then
    echo "empty" >> $FQLOGNAME
    else
    echo $ERRMSG|grep -o 'No such file or directory'|head -1 >> $FQLOGNAME
    fi
else
echo "success" >> $FQLOGNAME
fi

Please advice, Thanks

Upvotes: 1

Views: 413

Answers (2)

pjh
pjh

Reputation: 8209

You don't need to use grep to check if a string contains a substring. The built-in pattern matching in Bash is sufficient. This code should do something close to what you want:

if ERRMSG=$(cd /nonexist 2>&1) ; then
    echo 'process success'
elif [[ $ERRMSG == *'No such file or directory'* ]] ; then
    echo 'No such file or directory'
else
    echo 'empty'
fi >> "$FQLOGNAME"

See the Conditional Constructs section of the Bash Reference Manual for details of the pattern matching capabilities of [[...]].

I've retained the ERRMSG and FQLOGNAME variables, but note that it's best to avoid ALL_UPPERCASE variable names. There is a danger that they will clash with environment variables or Bash builtin variables. See Correct Bash and shell script variable capitalization.

To find error messages defined by a pattern in multi-line error messages, and only print the first one, you can use regular expression matching (=~) in [[...]]. To provide a concrete example, this code assumes that error messages consist of 'ERROR' followed by one or more spaces followed by a decimal number:

# Example function for testing
function dostuff
{
    printf 'Output line A\n'
    printf 'Encountered ERROR 29\n' >&2
    printf 'Output line B\n'
    printf 'Encountered ERROR 105\n' >&2
    printf 'Output line C\n'

    return 1
}

# Regular expression matching an error string
readonly error_rx='ERROR +[0-9]+'

if ERRMSG=$(dostuff 2>&1) ; then
    echo 'process success'
elif [[ $ERRMSG =~ $error_rx ]] ; then
    printf '%s\n' "${BASH_REMATCH[0]}"
else
    echo 'empty'
fi >> "$FQLOGNAME"

It appends 'ERROR 29' to the log file.

For more information about Bash's built-in regular expression matching see mklement0's answer to "How do I use a regex in a shell script?".

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 142080

Make it simpler and easier:

if ! ERRMSG=$(cd /nonexist 2>&1); then
     if <<<"$ERRMSG" grep -q 'No such file or directory'; then
           # if the error string contains the message 'No such file or directory'
           echo "empty" >> "$FQLOGNAME"
     else
           printf "Unhandled cd error: %s" "$ERRMSG" >> "$FQLOGNAME"
     fi
else
     echo "process success" >> "$FQLOGNAME"
fi
  1. if statements checks for the return status of a COMMAND. [ or test is just a command, which return a status. The return status of assignment is the same as command status. What I mean, is that out=$(cmd); if [ "$?" -eq 0 ]; then is the same as if out=$(cmd); then.
  2. Using HERE-strings is a bit better than echo "$string". Echo is not that much portable, better get used to printf "%s" "$string" which is a portable way. However HERE-strings puts additional EOF at the end of the stream, which sometimes breaks while read loops, but for most cases works ok.
  3. Don't if [ -z "$(echo smth | grep ..)" ]; then. You can just check grep return status, just if echo smth | grep ...; then or with HERE-strings if <<<"smth" grep -q ...; then or if grep -q ... file; then. The -q option which has --quiet or --silent alternatives makes grep produce no output.
  4. The quoting is not needed when assigning a variable from a single command substitution. tmp="$(...)" is just the same as tmp=$(...).

Upvotes: 0

Related Questions