Check if command error contains a substring

Viewed 2475

I have a lot of bash commands.Some of them fail for different reasons. I want to check if some of my errors contain a substring.

Here's an example:

#!/bin/bash

if [[ $(cp nosuchfile /foobar) =~ "No such file" ]]; then
    echo "File does not exist. Please check your files and try again."
else
    echo "No match"
fi

When I run it, the error is printed to screen and I get "No match":

$ ./myscript
cp: cannot stat 'nosuchfile': No such file or directory
No match

Instead, I wanted the error to be captured and match my condition:

$ ./myscript
File does not exist. Please check your files and try again.

How do I correctly match against the error message?

P.S. I've found some solution, what do you think about this?

out=`cp file1 file2 2>&1`
if [[ $out =~ "No such file" ]]; then
    echo "File does not exist. Please check your files and try again."
elif [[ $out =~ "omitting directory" ]]; then
    echo "You have specified a directory instead of a file"
fi
3 Answers

Both examples:

out=`cp file1 file2 2>&1`

and

case $(cp file1 file2 2>&1) in 

have the same issue because they mixing the stderr and stdout into one output which can be examined. The problem is when you trying the complex command with interactive output i.e top or ddrescueand you need to preserve stdout untouched and examine only the stderr. To omit this issue you can try this (working only in bash > v4.2!):

shopt -s lastpipe
declare errmsg_variable="errmsg_variable UNSET"

command 3>&1 1>&2 2>&3 | read errmsg_variable

if [[ "$errmsg_variable" == *"substring to find"* ]]; then
    #commands to execute only when error occurs and specific substring find in stderr
fi

Explanation

This line

command 3>&1 1>&2 2>&3 | read errmsg_variable

redirecting stderr to the errmsg_variable (using file descriptors trick and pipe) without mixing with stdout. Normally pipes spawning own sub-processes and after executing command with pipes all assignments are not visible in the main process so examining them in the rest of code can't be effective. To prevent this you have to change standard shell behavior by using:

shopt -s lastpipe

which executes last pipe manipulation in command as in the current process so:

| read errmsg_variable

assignes content "pumped" to pipe (in our case error message) into variable which resides in the main process. Now you can examine this variable in the rest of code to find specific sub-string:

if [[ "$errmsg_variable" == *"substring to find"* ]]; then
    #commands to execute only when error occurs and specific substring find in stderr
fi
Related