1

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

galih
  • 499
  • 1
  • 6
  • 16

2 Answers2

1

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?".

pjh
  • 6,388
  • 2
  • 16
  • 17
  • it's a sample error message, because i would like to execute java script that possibly return multiple error message if failed, therefore i need to grep and pick first match only, is it applicable with your solution to my case? – galih Sep 18 '18 at 02:29
  • I've added code that attempts to meet the clarified requirements. – pjh Sep 18 '18 at 13:06
0

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=$(...).
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • I'm not altogether convinced this is "simpler and easier". – tripleee Sep 17 '18 at 19:02
  • thanks, i will try this, but is this grep only result for first match? – galih Sep 18 '18 at 02:52
  • and what does it means with <<< ? sorry i'm not really familiar with bash scripting – galih Sep 18 '18 at 02:53
  • `<<<"$string"` is called HERE-string and it is the same as `echo "$string" | ` or more professional `printf "%s" "$string"`. If I understood you correctly, you wanted to print "empty" to the log file if the ERRMSG contains "No such file or directory". So the script does, reading from the second line: if ERRMSG contains/greps 'No such file or directory, then print empty to FQLOGNAME eles print unhandled cd error to FQLOGNAME. – KamilCuk Sep 18 '18 at 07:01