I have written this shell script to backup MySQL database to disk. It works fine but I am not well proficient in shell scripting. So what do you think can it be improved?
#!/bin/bash
#For taking backup
APPEND_CLI=3ドル
DIR=/media/storage/backup/db_backup/
DATESTAMP=$(date +%d-%m-%y-%H-%M)
DB_USER=backup
DB_PORT=2ドル
DB_PASS='readonly'
HOST=1ドル
if [[ `id -u` != 0 ]]; then
echo "Must be root to run script"
exit
fi
if test -z "$HOST"
then
echo "HOST not passed."
exit 1
fi
if test -z "$DB_PORT"
then
echo "PORT not passed."
exit 1
fi
# remove backups older than $DAYS_KEEP
DAYS_KEEP=7
find ${DIR}* -mtime +$DAYS_KEEP -exec rm -f {} \; 2> /dev/null
# create backups securely
umask 006
# list MySQL databases and dump each
DB_LIST=`$APPEND_CLI mysql -h $HOST -u $DB_USER -p"$DB_PASS" -e'show databases;'`
echo "Listing databases"
echo ${DB_LIST##Database}
DB_LIST=${DB_LIST##Database}
for DB in $DB_LIST;
do
if [[ "${DB}" == "performance_schema" ]]
then
echo "Skipping database ${DB}."
continue
fi
FILENAME=${DIR}${DB}-${DATESTAMP}.msql.${DB_PORT}.gz
echo "Initiating backup of $DB for ${HOST} on port ${DB_PORT}"
$APPEND_CLI mysqldump -h $HOST -P $DB_PORT -u $DB_USER -p"$DB_PASS" $DB --single-transaction | gzip > $FILENAME
echo "Done backing up ${FILENAME}"
done
1 Answer 1
Please use lower-case for Bash variables - upper-case is normally used for environment variables that communicate with child processes.
if [[ `id -u` != 0 ]]; then echo "Must be root to run script" exit fi
Error messages should go to the standard error stream (>&2
). And I think we should be exiting with a failure status in this case (since we didn't perform the requested backup).
We can make life easier for ourselves with a short function to print an error message and exit with failure. That looks like
die() {
echo >&2 "$@"
exit 1
}
Then our three tests become simpler:
[ "$(id -u)" = 0 ] || die "Must be root to run script"
[ -n "$HOST" ] || die "HOST not passed."
[ -n "$DB_PORT" ] || die "PORT not passed."
Instead of the continue
to skip over the performance_schema
database, we could exclude it from our results:
mysql -e 'show databases where `Database` not like "performance_schema"'
And we can avoid the need to remove the Database
column header by passing --skip-column-names
.
Since we're using Bash, which has arrays, we should use an array for the list of databases, particularly in case they might contain spaces in their names.
We can use read -a
to assign input lines to an array:
IFS=$'\n' read -d '' -r -a db_list \
< <(mysql --batch --skip-column-names -e 'show databases')
Then we can loop over the names:
for db in "${db_list[@]}"
do
filename=${dir}${db}-${datestamp}.msql.${db_port}.gz
echo "Initiating backup of $db for ${host} on port ${db_port}"
$append_cli mysqldump \
-h "$host" -P "$db_port" -u "$db_user" -p"$db_pass" "$db"
--single-transaction \
| gzip >$filename
echo "Done backing up ${filename}"
done
-
\$\begingroup\$ thanks I am wondering how can not delete the old back if its the last and only one in that backup directory. \$\endgroup\$Chang Zhao– Chang Zhao2022年02月18日 22:04:15 +00:00Commented Feb 18, 2022 at 22:04
-
\$\begingroup\$ You might be better leaving that logic out of your script and using something external (logrotate) to deal with that for you. It's probably easier to configure that than to write and test your own version. I was going to mention that, but got distracted. \$\endgroup\$Toby Speight– Toby Speight2022年02月18日 22:10:52 +00:00Commented Feb 18, 2022 at 22:10