質問

I want to write a bash function to test whether or not a mysqldump command succeeded. What I've tried so far is to test whether the dump file is zero size or not, like this:

l_mysqldump= # set your custom mysqldump path here, like /Applications/MAMP/Library/bin/mysqldump

${l_mysqldump:-mysqldump} -u$l_db_user -p$l_db_pass -h $l_db_host $l_db_name > latest-dump.mssql

if [[ -s latest-dump.mssql ]]
then
    echo "Success: not zero size."
else
    echo "Error: zero size."
    exit 1
fi

I decided to test this way, rather than to test the exit status of the mysqldump command, because when the default mysql server is down, running a mysqldump command gives this error:

mysqldump: Got error: 2002: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) when trying to connect

Even though it throws up this error, it still generates a 0 byte .mssql file. Also, the exit status is 0 no matter what. The script has to exit if the dump failed, and needs to communicate to the user that they need to either specify a different mysqldump binary or to activate their server.

The above piece of code works fine. However, I don't want mysqldump to generate an uncompressed dump file. I want to bzip it, like this:

${l_mysqldump:-mysqldump} -u$l_db_user -p$l_db_pass -h $l_db_host $l_db_name | bzip2 -c > latest-dump.mssql.bz2

The problem is, this breaks my [-s] bash test, because the .bz2 file it generates is 14 bytes. So even though the uncompressed .mssql file is size zero, the equivalent archive is not size zero.

So what's the best way to solve this? I could just generate an uncompressed dump file, test it, then bzip it after. I suspect there is a much smarter way to do this however.

役に立ちましたか?

解決

The real problem is that with a pipe, the default return status is the exit code of the last command (bzip2). To quote man bash:

   The return status of a pipeline is the exit status of the last command,
   unless  the  pipefail  option  is enabled.  If pipefail is enabled, the
   pipeline's return status is the value of the last  (rightmost)  command
   to  exit  with a non-zero status, or zero if all commands exit success‐
   fully.

If you want to report failure if any command of the pipe failed, you have to set the pipefail option. Assuming bash, here is an example (I don't have mysql server running on localhost):

sylvain@daal:~$ set -o pipefail
sylvain@daal:~$ if mysqldump -u root -p db | bzip2 -c > latest-dump.mssql.bz2; then echo ok; else echo non ok; fi
Enter password: 
mysqldump: Got error: 2002: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2) when trying to connect
non ok

For your particular case, you will write something like:

set -o pipefail

if ${l_mysqldump:-mysqldump} -u$l_db_user -p$l_db_pass -h $l_db_host $l_db_name | bzip2 -c > latest-dump.mssql.bz2
    then
      ...
    fi


BTW: "mysqldump command gives this error [..] Even though it throws up this error, it still generates a 0 byte .mssql file."

To be more precise, since you use shell redirection, it is the host shell that create the file. Before it even tried to run the command.

他のヒント

How about checking for common error strings in the mysql command? You could place your command inside process subtitution while redirecting stdout and stderr:

IFS= read -rd '' OUTPUT < <(${l_mysqldump:-mysqldump} -u$l_db_user -p$l_db_pass -h $l_db_host $l_db_name 2>&1 > >(bzip2 -c > latest-dump.mssql.bz2))

Then check the output normally with grep, case, [[ == ]], or [[ =~ ]] e.g.

if echo "$OUTPUT" | grep "error" >/dev/null; then
    (failure)
else
    (success)
fi

case "$OUTPUT" in
*"error"*|*another_error_pattern*|...)
  (failure)
  ;;
*)
  (success)
  ;;
esac

Perhaps another way is to check if if the size of the output file is larger than the default size 14, but I think it's less certain with that.

${l_mysqldump:-mysqldump} -u$l_db_user -p$l_db_pass -h $l_db_host $l_db_name > latest-dump.mssql

if IFS= read SIZE < <(stat -c "%s" latest-dump.mssql) && [[ SIZE -gt 14 ]]; then
   (success)
else
   (fail)
fi

It's also a good idea to quote your variables properly to keep it safe from unexpected expansion:

"${l_mysqldump:-mysqldump}" "-u$l_db_user" "-p$l_db_pass" -h "$l_db_host" "$l_db_name" > latest-dump.mssql

Update: Seeing your update, I think the ideal string for your error output pattern would be "mysqldump: Got error:" or just "Got error:". e.g.

IFS= read -rd '' OUTPUT < <("${l_mysqldump:-mysqldump}" -u"$l_db_user" -p"$l_db_pass" -h "$l_db_host" "$l_db_name" 2>&1 > >(bzip2 -c > latest-dump.mssql.bz2))

if echo "$OUTPUT" | grep "Got error:" >/dev/null; then
...

if grep "Got error:" >/dev/null <<< "$OUTPUT"; then
...

case "$OUTPUT" in
*"Got error:"*)
...

if [[ $OUTPUT == *"Got error:"* ]]; then
...
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top