Bash脚本:`exit 0`无法退出

时间:2013-11-25 03:44:06

标签: bash exit hang

所以我有这个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用于向服务器发送停止/启动命令以进行自动保存。最后,一旦它从服务器收到服务器已完成保存的消息,它就是tar的数据目录并再次开始保存。

我在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

但它挂在那里。注意“完成”回声是怎么回事,但“done2”失败了。导致它挂起exit 0的东西。

ADDENDUM 为了避免将来看到此内容的人产生混淆,它会在退出行挂起并且永远不会返回到命令提示符。不确定我的原始描述是否足够清晰。

有什么想法?这是整个脚本,没有其他任何事情发生,我直接从bash中调用它。

3 个答案:

答案 0 :(得分:8)

这是一个较小的,自包含的示例,表现出相同的行为:

echo foo > file
tail -f file | while read; do exit; done

问题在于,由于管道的每个部分都在子shell中运行,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完成,并且它不会在子shell中运行,因此exit实际上将退出整个脚本。

答案 1 :(得分:1)

  

但它挂在那里。注意“完成”回声是怎么回事,但“done2”失败了。

由于done2已使用exit 0结束了您的脚本,因此根本不会打印

return code 0

答案 2 :(得分:0)

我不知道循环中bash子shell的细节,但通常退出循环的合适方法是使用“break”命令。在某些情况下这还不够(你真的需要退出程序),但重构该程序可能是解决这个问题的最简单(最安全,最便携)的方法。它还可以提高可读性,因为人们不希望程序在循环中退出。