所以我有这个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中调用它。
答案 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”命令。在某些情况下这还不够(你真的需要退出程序),但重构该程序可能是解决这个问题的最简单(最安全,最便携)的方法。它还可以提高可读性,因为人们不希望程序在循环中退出。