Понимание цикла WHILE в bash

Вопрос или проблема

Я пытаюсь удалить строки из нескольких таблиц в нескольких базах данных. Прикрепленный скрипт показывает только одну базу данных, но массив содержит список из четырех баз данных.

#!/bin/bash
declare -a DB_ARRAY=("delete_test")
where_clause="where crt_tm between date_format('2022-04-01 00:00:00','%Y-%m-%d 00:00:00') and date_format('2023-03-31 23:59:59','%Y-%m-%d 23:59:59')"
log='/data/scripts_logs/delete.log'

#Следующие пути входа используются в операторе CASE ниже.

#[path1]
path1='pt_archiver'

delete_from_wallet_order_deposit(){

for DB in "${DB_ARRAY[@]}"
do
        for file in `find /data/bkp/${DB}/ -type f -name "*.sql" |  awk -F "https://unix.stackexchange.com/" '{ print $5 }'`
        do
                table_name=`echo ${file} | awk -F '.' '{ print $1 }'`
                #echo ${DB}.${table_name}

                case ${DB} in
                delete_test)
                        count=`mysql --login-path=${path1} --silent --skip-column-names ${DB} -e "select count(*) from ${table_name} ${where_clause};"`
                        echo ${count}

                        if [ ${count} != 0 ]
                        then
                                echo "Общее количество строк в ${DB}.${table_name} составляет ${count}." >> ${log}
                                echo "Эти строки будут удалены." >> ${log}

                                while [ ${count} -gt 0 ]
                                do
                                        mysql --login-path=${path1} --init-command="SET SQL_LOG_BIN = 0" ${DB} -e "delete from ${table_name} ${where_clause} order by crt_tm limit 5000; select sleep(5);" &>> ${log}
                                done

                                if [ $? -eq 0 ]
                                then
                                        echo "Успешно удалено ${count} строк из ${DB}.${table_name}." >> ${log}
                                        else
                                echo "Проверьте журнал ошибок."
                                fi
                        fi
                ;;

                esac
        done
done
}

В каждой таблице нужно удалить около 40000 строк, и таких таблиц несколько.

Я понимаю, что, когда count > 0, он заходит в цикл IF, а затем, пока count больше 0, он должен войти в цикл WHILE и начать удалять 5000 строк, и должен выйти, когда строки удалены, но он застревает в бесконечном цикле. Я явно что-то упускаю. Пожалуйста, помогите мне понять, что именно я упускаю. Также может быть более эффективный способ сделать это, но это не то, что я ищу. Я просто пытаюсь понять, как работает цикл WHILE. Я использовал цикл for, и я понимаю его, но явно не цикл WHILE.

Должен ли я добавить еще один select после delete (в WHILE) и снова захватить count? Это то, что я упускаю?

Ответ или решение

Для того чтобы понять, как работает цикл WHILE в Bash, важно обратиться к структуре вашего скрипта, который вы предоставили, а также к основным принципам работы циклов в Bash.

Что такое цикл WHILE?

Цикл WHILE в Bash выполняет набор команд до тех пор, пока заданное условие истинно. Его общая форма выглядит следующим образом:

while условие; do
    команды
done

В вашем скрипте вы используете цикл WHILE для удаления строк из таблиц в базе данных. Но почему он зацикливается? Давайте это выясним.

Разбор вашего кода

Ваш цикл WHILE такой:

while [ ${count} -gt 0 ]
do
    mysql --login-path=${path1} --init-command="SET SQL_LOG_BIN = 0" ${DB} -e "delete from ${table_name} ${where_clause} order by crt_tm limit 5000; select sleep(5);" >> ${log}
done

Здесь переменная count проверяется на то, больше ли она нуля. Однако, вы нигде не обновляете значение переменной count внутри цикла. Это и приводит к бесконечному циклу. count остается прежним, и цикл продолжает выполняться бесконечно, так как условие [ ${count} -gt 0 ] всегда истинно, если только вы не измените значение count.

Как исправить ситуацию

Чтобы исправить ваш код, вам действительно нужно обновить count в каждом цикле WHILE. Для этого можно добавить еще один запрос, чтобы получить текущее количество строк, которые необходимо удалить, перед тем как завершить одну итерацию цикла:

while [ ${count} -gt 0 ]
do
    mysql --login-path=${path1} --init-command="SET SQL_LOG_BIN = 0" ${DB} -e "delete from ${table_name} ${where_clause} order by crt_tm limit 5000; select sleep(5);" >> ${log}

    # Обновляем значение count
    count=`mysql --login-path=${path1} --silent --skip-column-names ${DB} -e "select count(*) from ${table_name} ${where_clause};"`
done

Таким образом, после каждого удаления вы будете обновлять count, и цикл завершится, когда количество оставшихся строк станет равным нулю.

Заключение

Цикл WHILE — это мощный инструмент в Bash, который может помочь при выполнении повторяющихся задач до тех пор, пока не будет достигнуто определенное условие. Важно всегда следить за изменением переменных, используемых в условиях циклов, чтобы избежать бесконечных циклов, как в вашем случае.

Также, хотя в вашем случае вы ищете понимания цикла WHILE, стоит отметить, что для улучшения производительности вашего скрипта можно рассмотреть другие конструкции или даже другие языки программирования, если задачи становятся слишком громоздкими.

Если у вас возникнут дополнительные вопросы или нужна дальнейшая помощь, не стесняйтесь обращаться!

Оцените материал
Добавить комментарий

Капча загружается...