#!/usr/bin/env bash working_dir="$(dirname $(realpath $0))" conf_file="${working_dir}/conf/mysql_credetials.conf" tmp_log_file="$(mktemp)" # ------------- # - Variable settings # ------------- MYSQL_CREDENTIALS_GIVEN=false ALL_DATABASES=false DATABASE_NAME_NEEDED=true # ------------- # --- Some functions # ------------- usage() { [[ -n "$1" ]] && error "$1" [[ $terminal ]] && echo -e " \033[1mUsage:\033[m $(basename $0) [DB-Name] \033[1mDescription\033[m Script repairs (and reorganizes) all tables of the given databases by executing MySQL command 'REPAIR TABLE'. If this was successfully, also 'OPTIMIZE TABLE' command will be triggered. If no database was given at command line all tables of ALL databases are checked. \033[1mOptions\033[m No Options available \033[1mFiles\033[m $conf_file: Configuration file " clean_up 1 } clean_up() { # Perform program exit housekeeping rm -f $tmp_log_file exit $1 } echononl(){ if $terminal ; then echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n "$*\\c" 1>&2 else echo -e -n "$*" 1>&2 fi rm /tmp/shprompt$$ fi } fatal(){ echo "" if $terminal ; then if [[ -n "$*" ]] ; then echo -e " [ \033[31m\033[1mFatal\033[m ]: $*" echo "" echo -e " \033[31m\033[1mScript will be interrupted.\033[m\033[m" else echo -e " \033[31m\033[1mFatal error\033[m: \033[1mScript will be interrupted.\033[m" fi else if [[ -n "$*" ]] ; then echo " [ Fatal ]: $*" echo "" echo " Script was terminated.." else echo " Fatal error: Script was terminated.." fi fi echo "" clean_up 1 } error(){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mError\033[m ]: $*" else echo " [ Error ]: $*" fi echo "" } warn (){ if $terminal ; then echo "" echo -e " [ \033[33m\033[1mWarning\033[m ]: $*" echo "" else echo " [ Warning ]: $*" fi } info (){ if $terminal ; then echo "" echo -e " [ \033[32m\033[1mInfo\033[m ]: $*" echo "" else echo " [ Info ]: $*" fi } echo_ok() { if $terminal ; then echo -e "\033[80G[ \033[32mok\033[m ]" fi } echo_failed(){ if $terminal ; then echo -e "\033[80G[ \033[1;31mfailed\033[m ]" fi } echo_skipped() { if $terminal ; then echo -e "\033[80G[ \033[37mskipped\033[m ]" fi } is_number() { return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1); # - also possible # - #[[ ! -z "${1##*[!0-9]*}" ]] && return 0 || return 1 #return $([[ ! -z "${1##*[!0-9]*}" ]]) } trim() { local var="$*" var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters echo -n "$var" } detect_mysql_version () { _MYSQLD_VERSION="$(mysqld -V 2>/dev/null)" if [[ -z "$_MYSQLD_VERSION" ]]; then fatal "No installed MySQL server or distribution found!" elif [[ "$_MYSQLD_VERSION" =~ MariaDB ]]; then MYSQL_CUR_DISTRIBUTION="MariaDB" elif [[ "$(basename "$(realpath "/usr/local/mysql")")" =~ percona- ]]; then MYSQL_CUR_DISTRIBUTION="Percona" elif [[ "$(basename "$(realpath "/usr/local/mysql")")" =~ mysql- ]]; then MYSQL_CUR_DISTRIBUTION="MySQL" fi MYSQL_VERSION="$(echo $_MYSQLD_VERSION | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?" | head -n 1)" MYSQL_MAJOR_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f1)" MYSQL_MINOR_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f2)" MYSQL_PATCH_LEVEL="$(echo $MYSQL_VERSION | cut -d '.' -f3)" MYSQL_MAIN_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f1,2)" } trap clean_up SIGHUP SIGINT SIGTERM # - Is this script running on terminal ? # - if [[ -t 1 ]] ; then terminal=true else terminal=false fi echononl " Get MySQL command.." mysql_command="$(which mysql)" if [[ $? -eq 0 ]]; then echo_ok else if [[ -x "/usr/local/mysql/bin/mysql" ]]; then mysql_command="/usr/local/mysql/bin/mysql" echo_ok else echo_failed fatal "No binary 'mysql' found!" fi fi # - Print help? # - if [[ "$(trim $*)" = "-h" ]] || [[ "$(trim $*)" = "--help" ]] ; then usage fi # ------------- # - Get Database from commandline # ------------- if [[ -n "$1" ]] ; then DATABASE_NAME_NEEDED=false DATABASE_NAME="$1" else DATABASE_NAME="" fi # ------------- # - Load Settings from configuration file # ------------- if $terminal ; then echo "" fi echononl " Loading configuration settings from $(basename ${conf_file}).." if [[ -f "$conf_file" ]]; then source "$conf_file" > $tmp_log_file 2>&1 if [[ $? -eq 0 ]]; then echo_ok else echo_failed fatal "$(cat $tmp_log_file)" fi else echo_skipped if $terminal ;then warn "No Configuration File found. Loading defaults.." fi fi declare -i index_arr=0 if [[ -n "$mysql_credential_args" ]]; then MYSQL_CREDENTIAL_ARGS="$mysql_credential_args" MYSQL_CREDENTIALS_GIVEN=true fi # - Get MySQL Version if paralle installations # - if [[ ${#mysql_credential_args_arr[@]} -gt 0 ]] ; then echo "" echo -e "\033[32m--\033[m" echo "" echo "Which installation contains the database to be repaired?" echo "" echo "" declare -a _tmp_arr=() while [[ $index_arr -lt ${#mysql_credential_args_arr[@]} ]] ; do IFS=':' read -a _val_arr <<< "${mysql_credential_args_arr[$index_arr]}" mysql_version="${_val_arr[0]}" mysql_credential_args="${_val_arr[1]}" mysql_dist_string="$(${mysql_command} $mysql_credential_args -N -s -e "SELECT VERSION()" 2> /dev/null)" if [[ $? -ne 0 ]] ; then if [[ "$(cat $tmp_log_file)" =~ "unknown variable 'login-path" ]] ; then if [[ -x "/usr/local/mysql/bin/mysql" ]] ; then mysql_dist_string="$(/usr/local/mysql/bin/mysql $mysql_credential_args -N -s -e "SELECT VERSION()" 2> /dev/null)" fi fi fi if [[ "$mysql_dist_string" =~ MariaDB ]]; then mysql_dist="MariaDB $mysql_version" else mysql_dist="MySQL/Percona $mysql_version" fi echo -e " [\033[33m$index_arr\033[m] \033[33m$mysql_dist\033[m" _temp_arr[${index_arr}]="$mysql_credential_args" #_temp_arr+=("$mysql_credential_args") (( index_arr++ )) done _OK=false echo "" echononl "Eingabe: " while ! $_OK ; do read _IN if is_number "$_IN" && [[ -n ${_temp_arr[$_IN]} ]]; then MYSQL_CREDENTIAL_ARGS="${_temp_arr[$_IN]}" _OK=true else echo "" echo -e "\tFalsche Eingabe !" echo "" echononl "Eingabe: " fi done MYSQL_CREDENTIALS_GIVEN=true fi if $DATABASE_NAME_NEEDED ; then echo "" echo -e "\033[32m--\033[m" echo "" echo "Insert Database name which should be repaired.." echo "" echo -e " \033[33mLeave empty to repair tables of all databases\033[m" echo "" echononl "Database name: " read DATABASE_NAME #while [ "X$DATABASE_NAME" = "X" ] ; do # echo -e "\n\t\033[33m\033[1mEingabe erforderlich.\033[m\n" # echononl "Database name: " # read DATABASE_NAME #done fi if $MYSQL_CREDENTIALS_GIVEN ; then # - Get MySQL Version # - echo "" echo -e "\033[32m--\033[m" echo "" echononl " Get MySQL Version" _version="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "SELECT VERSION()" 2> $tmp_log_file)" if [[ $? -ne 0 ]] ; then if [[ "$(cat $tmp_log_file)" =~ "unknown variable 'login-path" ]] ; then if [[ -x "/usr/local/mysql/bin/mysql" ]] ; then mysql_command="/usr/local/mysql/bin/mysql" else echo_failed fatal "$(cat $tmp_log_file)" fi _version="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "SELECT VERSION()" 2> $tmp_log_file)" if [[ $? -ne 0 ]] ; then echo_failed fatal "$(cat $tmp_log_file)" else echo_ok fi else echo_failed fatal "$(cat $tmp_log_file)" fi else echo_ok fi IFS='.' read -r -a version_arr <<< "$_version" declare -i MAJOR_VERSION="${version_arr[0]}" declare -i MINOR_VERSION="${version_arr[1]}" _path_level="${version_arr[2]}" declare -i PATCH_LEVEL="${_path_level%%-*}" ## - Get current MySQL Distribution ## - echononl " Get MySQL distribution .." if [[ -z "$_version" ]]; then echo_failed fatal "No installed MySQL server or distribution found!" elif [[ "$_version" =~ MariaDB ]]; then MYSQL_CUR_DISTRIBUTION="MariaDB" else MYSQL_CUR_DISTRIBUTION="MySQL" fi echo_ok echo "" echo -e "\033[32m--\033[m" echo "" else detect_mysql_version MAJOR_VERSION="$MYSQL_MAJOR_VERSION" MINOR_VERSION="$MYSQL_MINOR_VERSION" PATCH_LEVEL="$MYSQL_PATCH_LEVEL" if [[ "$MYSQL_CUR_DISTRIBUTION" = "MariaDB" ]] && ([[ $MAJOR_VERSION -gt 10 ]] \ || ( [[ $MAJOR_VERSION -eq 10 ]] && [[ $MINOR_VERSION -gt 3 ]] )) ; then if [[ -S "/tmp/mysql.sock" ]]; then MYSQL_CREDENTIAL_ARGS="-u root -S /tmp/mysql.sock" elif [[ -S "/run/mysqld/mysqld.sock" ]]; then mysql_credential_args="-u root -S /run/mysqld/mysqld.sock" elif [[ -S "/var/run/mysqld/mysqld.sock" ]]; then MYSQL_CREDENTIAL_ARGS="-u root -S /var/run/mysqld/mysqld.sock" else fatal "Parameter 'MYSQL_CREDENTIAL_ARGS' cannot be determined automated. Use configuration file "$conf_file" to set parameter manually." fi else if $(${mysql_command} --login-path=local -e ";" > /dev/null 2>&1) ; then MYSQL_CREDENTIAL_ARGS="--login-path=local" elif [[ -f "/usr/local/mysql/sys-maint.cnf" ]] ; then MYSQL_CREDENTIAL_ARGS="--defaults-file=/usr/local/mysql/sys-maint.cnf" elif [[ -f "/etc/mysql/debian.cnf" ]] ; then MYSQL_CREDENTIAL_ARGS="--defaults-file=/etc/mysql/debian.cnf" else fatal "Parameter 'MYSQL_CREDENTIAL_ARGS' cannot be determined automated. Use configuration file "$conf_file" to set parameter manually." fi fi fi echo "" echo "" echo -e "\033[32m\033[1m====================\033[m" echo "Settings: Repair tables of MySQL Database '$DATABASE_NAME'" echo -e "\033[32m\033[1m====================\033[m" echo "" echo " MySQL Distribution...........: $MYSQL_CUR_DISTRIBUTION" echo " MySQL Version................: ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}" echo " MySQL Credentials............: $MYSQL_CREDENTIAL_ARGS" echo "" echo " MySQL commnd.................: ${mysql_command}" echo "" if [[ -n "$DATABASE_NAME" ]]; then echo " Database name................: $DATABASE_NAME" else echo -e " Database name................: -- \033[033mall databases\033[m --" fi echo "" echo "" echo -e -n "\033[1mContinue repairing tables with above settings? [y/n]:\033[m " read OK while [[ "X${OK}X" = "XX" ]] ; do echo "" echo -e -n "\033[1mContinue with above settings? [y/n]:\033[m " read OK done if [[ "${OK,,}" != 'yes' ]] && [[ "${OK,,}" != 'y' ]]; then fatal "Abort by user request." fi declare -i length_table_name declare -i number_blank_signd declare -i index_i _all_success=true DATABASES="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "show databases")" if [[ -z "$DATABASE_NAME" ]] ; then if $terminal ; then echo "" echo -e " [ \033[37m\033[1m$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}\033[m ]: repair (and optimize) tables of ALL databases at host '$(hostname -f)'." fi ALL_DATABASES=true else found=false for db in $DATABASES ; do if [[ "$db" = "$DATABASE_NAME" ]]; then DATABASES="$DATABASE_NAME" found=true break fi done if ! $found ; then warn "No Database '$DATABASE_NAME' found at $MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}" clean_up 0 fi fi length_table_name=0 for db in $DATABASES ; do [ "$db" = "information_schema" ] && continue [ "$db" = "performance_schema" ] && continue [ "$db" = "mysql" ] && continue if $terminal ;then echo "" if [[ -n "$DATABASE_NAME" ]] ; then echo -e " [$(date)] Repair (and optimize) tables in database '${db}'.." else echo -e " [ \033[37m\033[1m$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}\033[m $(date) ] Repair (and optimize) tables in database '${db}'.." fi fi TABLES="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS $db -N -s -e "show tables" 2> $tmp_log_file )" if [[ $? -ne 0 ]]; then _all_success=false error "Getting tables of database '${db}' failed.\n $(cat "$tmp_log_file")" error_messages_arr+=("Getting tables of database '${db}' failed.") continue fi for table in $TABLES ; do # - Ommit InnoDB tables # - _engine="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$db' AND TABLE_NAME = '$table'")" if [[ "${_engine,,}" = 'innodb' ]] ; then echo -e " [$(date)] Ommit table '$table' - The storage engine (InnoDB) doesn't support repair" continue fi if $terminal ; then blank_signs="" if [[ $length_table_name -gt ${#table} ]]; then number_blank_sign=$(expr $length_table_name - ${#table}) index_i=0 while [[ $index_i -lt $number_blank_sign ]] ; do blank_signs="$blank_signs " (( index_i++ )) done echo -en "\033[1G" fi echo -en "\033[1G \033[32mRepair table \033[1m$table\033[m$blank_signs\033[1G" fi length_table_name=${#table} ${mysql_command} $MYSQL_CREDENTIAL_ARGS $db -N -s -e "REPAIR TABLE \`$table\`" > $tmp_log_file 2>&1 if [[ $? -ne 0 ]]; then _all_success=false error "Repairing table '$table' failed.\n$(cat "$tmp_log_file")" error_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Error while repairing table '${table}' of database '$db'.") else ${mysql_command} $MYSQL_CREDENTIAL_ARGS $db -N -s -e "OPTIMIZE TABLE \`$table\`" > $tmp_log_file 2>&1 if [[ $? -ne 0 ]]; then error "Reoptimizing table \"${table}\" of database \"$db\" failed.\n$(cat "$tmp_log_file")" error_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Error while (re-)optimizing table '${table}' of database '$db'.") fi fi done if $terminal ;then echo -en "\033[1G [$(date)] End repairing tables of database '${db}'.." echo fi done if $_all_success ; then if $ALL_DATABASES ; then info_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Repairing MySQL tables of all databases was successful.") else info_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Repairing MySQL tables of database '${db}' was successful.") fi fi if $terminal && $ALL_DATABASES ; then echo "" echo -e " [ \033[37m\033[1m$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}\033[m ]: Finished repairing MySQL databases at host $(hostname -f)." echo "" fi if [[ ${#info_messages_arr[@]} -gt 0 ]]; then for msg in "${info_messages_arr[@]}" ; do if $terminal ; then info "$msg" fi done fi if [[ ${#error_messages_arr[@]} -gt 0 ]]; then for msg in "${error_messages_arr[@]}" ; do if $terminal ; then error "$msg" fi done fi clean_up 0