5

I want to zip the sql dump if and only if mysqldump does not produce any error. I try to put the wrong password, but it still perform the gzip.

 mysqldump -u username -ppassword dbname |& if [ $? == 0 ]; then gzip > test.gz; else echo "error"; fi 

What is wrong with my command or is there any better solution?

1
  • When you use a pipe as opposed to a temporary file you will not wait for the end of the command. IMHO it does not hurt to compress partial/no output in error case. There is not much you can do anyway. Commented Aug 7, 2017 at 0:47

5 Answers 5

5

I think you could remove |& (the reason being you do not need to pipe either the stdout or stderr into the next conditional, if anything you can just use ';' as a command separator). Something like this should probably work:

#!/bin/bash mysqldump -u myuser -p mypasswd > mydb.dump if [[ $? -eq 0 ]]; then gzip mydb.dump else echo >&2 "DB backup failed" exit 1 fi 

Edit: to check the success of the gzip you could do something like this:

mysqldump -u myuser -p mypasswd | gzip > mydb.dump.gz && echo "success" || echo "failure" 

However this would report success even if mysqldump failed, i.e. if the backup failed for most reasons other than gzip filling up the disk.

3
  • So, it is not possible to do it without using temporary file? Commented Aug 6, 2017 at 15:20
  • If my understanding is correct, you want to directly pipe the output into a gzip format. The problem is that since checking the exit code requires the process to finish first, I don't think placing a check there will work. Logically speaking, how would you know the operation was successful until it completed fully? Commented Aug 6, 2017 at 15:57
  • If you just need reporting you could always add a check at the end. If there is a way of doing this I'd be interested too :) Commented Aug 6, 2017 at 16:03
3

You can also

mysqldump -someparams dump.sql && gzip dump.sql || echo "Backup failed" 

Or

mysqldump -someparams dump.sql [[ $? == 0 ]] && gzip dump.sql || echo "Backup failed " 
2
  • So, it is not possible to do it without using temporary file? Commented Aug 6, 2017 at 15:20
  • With temporary file you mean a shell script? The first line i write you can just paste to the shell and it will work :) Commented Aug 6, 2017 at 15:24
3

A pipe executes the two sides in parallel. The way you're attempting to do this is conceptually impossible: you can't test the status of the mysqldump command until it's finished if you do this test in parallel with the execution of mysqldump. You need to run mysqldump, wait for it to finish, and then decide whether to run gzip.

Since mysqldump has to finish running, its output has to go somewhere. Presumably you expect the output to be large since you're compressing it. Therefore the sensible option is to compress it. So compress the output unconditionally.

mysqldump -u username -ppassword dbname | gzip > test.gz 

Note that I used |, not |&. Using |& here makes no sense: if there are any error messages, they'd end up mixed with the dump and it would be impossible to restore the dump.

The problem that remains to be solved is detecting whether mysqldump succeeded. Assuming that this is a bash or ksh script (i.e. it begins with #!/bin/bash or #!/bin/ksh or the like, not with #!/bin/sh), set the pipefail option, so that the pipeline fails if any part fails. (By default, the status of a pipeline is the status of its rightmost command and the other commands' status is ignored.)

#!/bin/bash set -o pipefail -o errexit tmp="mydump.tmp.$$.gz" trap 'rm -f "$tmp"' ERR INT TERM HUP mysqldump … | gzip >"$tmp" mv "$tmp" mydump.gz 

Setting the errexit option ensures that if the pipeline fails then the script exits at that point (with the same error status as the pipeline). Thus a file called mydump.gz is only created if the dump was successful. The trap command sets up a trap so that if the script fails or is killed by one of the listed signals then the temporary file is deleted.

2
  • 1
    This is the most correct answer. For example, if one wanted to chain several commands together, ensuring that a pipe to gzip failed, it could look like this: date && set -o pipefail -o errexit && mysqldump --compress --single-transaction db_name | gzip > "db_name.sql.gz" && date. Without setting the bash options, the date call at the end of the chain will run; with the bash settings, the date call will not run, which is what OP wants. Commented Jan 17, 2018 at 16:44
  • thanks @danemacmillan, exactly what I needed! Commented Feb 17, 2023 at 18:57
2

You could also use bash build-in table $PIPESTATUS[x] to trap errors.

for example :

$ cmd1 | cmd2 | cmd3 | .. 
  • status of cmd1 will be in variable $PIPESTATUS[0]
  • status of cmd2 will be in $PIPESTATUS[1]

and so on..

Then you'd see which command has failed

0

If you want to avoid use of a temporary file another solution is to delete the resultant file on error:

( mysqldump ... || rm -f test.gz ) | gzip > test.gz 

Or, if you want to have an explicit flag, something like this:

rm -f test.ok ( mysqldump ... && touch test.ok ) | gzip > test.gz if [[ $? -eq 0 && -r test.ok ]]; then echo it worked else echo something went wrong fi 

You must log in to answer this question.