Bash скрипт: `exit 0` не может выйти
Итак, у меня есть этот скрипт Bash:
#!/bin/bash
PID=`ps -u ...`
if [ "$PID" = "" ]; then
echo $(date) Server off: not backing up
exit
else
echo "say Server backup in 10 seconds..." >> fifo
sleep 10
STARTTIME="$(date +%s)"
echo nosave >> fifo
echo savenow >> fifo
tail -n 3 -f server.log | while read line
do
if echo $line | grep -q 'save complete'; then
echo $(date) Backing up...
OF="./backups/backup $(date +%Y-%m-%d\ %H:%M:%S).tar.gz"
tar -czhf "$OF" data
echo autosave >> fifo
echo "$(date) Backup complete, resuming..."
echo "done"
exit 0
echo "done2"
fi
TIMEDIFF="$(($(date +%s)-STARTTIME))"
if ((TIMEDIFF > 70)); then
echo "Save took too long, canceling backup."
exit 1
fi
done
fi
По сути, сервер принимает данные из fifo и выводит их на server.log. Fifo используется для отправки команд остановки / запуска на сервер для автосохранения. В конце, как только он получает от сервера сообщение о том, что сервер завершил сохранение, он хранит каталог данных и снова запускает сохранение.
Это на exit 0
линия, что у меня проблемы. Все работает нормально, но я получаю этот вывод:
srv:scripts $ ./backup.sh
Sun Nov 24 22:42:09 EST 2013 Backing up...
Sun Nov 24 22:42:10 EST 2013 Backup complete, resuming...
done
Но там висит. Обратите внимание на то, как "готово" отражается, но "выполнено2" не удается. Что-то заставляет его висеть на exit 0
,
ДОПОЛНЕНИЕ: просто чтобы избежать путаницы для людей, которые смотрят на это в будущем, оно висит на линии выхода и никогда не возвращается в командную строку. Не уверен, был ли я достаточно ясен в моем первоначальном описании.
Какие-нибудь мысли? Это весь сценарий, больше ничего не происходит, и я звоню прямо из bash.
3 ответа
Вот небольшой автономный пример, демонстрирующий то же поведение:
echo foo > file
tail -f file | while read; do exit; done
Проблема в том, что, поскольку каждая часть конвейера работает в подоболочке, exit
только выходит из while read
цикл, а не весь сценарий.
Затем он будет висеть до tail
находит новую строку, пытается записать ее и обнаруживает, что канал сломан.
Чтобы исправить это, вы можете заменить
tail -n 3 -f server.log | while read line
do
...
done
с
while read line
do
...
done < <(tail -n 3 -f server.log)
Путем перенаправления из процесса подстановки поток не должен ждать tail
закончить, как это было бы в конвейере, и он не будет работать в подоболочке, так что exit
будет фактически выходить из всего сценария.
Но там висит. Обратите внимание на то, как "готово" отражается, но "выполнено2" не удается.
done2
не будет печататься вообще с exit 0
уже закончил ваш скрипт return code 0
,
Я не знаю подробностей о bash-подоболочках внутри циклов, но обычно правильный выход из цикла - использовать команду "break". В некоторых случаях этого недостаточно (вам действительно нужно выйти из программы), но рефакторинг этой программы может быть самым простым (самым безопасным, наиболее переносимым) способом решения этой проблемы. Это также может улучшить удобочитаемость, потому что люди не ожидают, что программы завершатся в середине цикла.