背景
程序员在日常工作中,为了解放人力提高效率,常常需要把一些周期性的任务例行化执行,比如每天发送一封数据报表邮件,每小时备份一次日志文件等。常用的技术方案是写一个shell脚本,然后通过配置linux的crontab来定时执行脚本(关于crontab,太过于基础,本文不做讲解)。
老大年前给要一份报表数据,我拼拼凑凑跑出来发给老大,大年初4在吃吃喝喝享受生活的时候老大又要近几天的数据……,沟通后,和老大达成一致,每周汇总一次近一周的数据。
问题
写了一个汇总数据并且发送邮件的shell脚本(/tmp/email.sh),手工执行是成功的,但是通过crontab执行却总是失败。
shell脚本如下:
#!/bin/bash
function getDay() {
# 数据统计核心逻辑
echo "$DATE" "$data1" "$data2" "$data3" >> /tmp/lastweek.txt
}
rm /tmp/lastweek.txt
echo "日期 第一列 第二列 第三列" >> /tmp/lastweek.txt
for (( i = 7; i > 0 ; i--));do
DATE="`date -d "$i days ago" '+%Y%m%d'`"
getDay $DATE
done
#发送邮件
START_DATE="`date -d "7 days ago" '+%Y%m%d'`"
END_DATE="`date -d "yesterday" '+%Y%m%d'`"
mail -s "数据统计报表["$START_DATE"~"$END_DATE"]" hello@163.com < /tmp/lastweek.txt
手工执行正确邮件如下:
crontab执行发送失败的邮件如下:
分析
crontab执行发送的错误邮件,标题中文部分为乱码,怀疑是环境变量LANG不支持中文,于是来简单测试一下:
#!/bin/bash
echo $LANG >> /tmp/test_out
crontab定时执行输出结果为:
[空]
直接执行输出结果为:
en_US.UTF-8
解决
方案就很明确了,强制设置环境变量LANG的值即可,在原有shell脚本(/tmp/email.sh)中添加如下语句:
export LANG="zh_CN.UTF-8"
再次使用crontab执行,邮件正常发送。
知识点
不要假定crontab知道所需要的特殊环境变量,它其实并不知道。所以你要保证在shelll脚本中提供所有必要的路径和环境变量,除了一些自动设置的全局变量。所以注意如下3点:
1)脚本中涉及文件路径时写全局路径; 2)脚本执行要用到java或其他环境变量时,通过source命令引入环境变量,如:
cat start_cbp.sh
#!/bin/sh
source /etc/profile
export RUN_CONF=/home/d139/conf/platform/cbp/cbp_jboss.conf
/usr/local/jboss-4.0.5/bin/run.sh -c mev &
3)当手动执行脚本OK,但是crontab死活不执行时。这时必须大胆怀疑是环境变量惹的祸,除了在shell脚本中通过source或者export命令引入变量,也可以尝试在crontab中直接引入环境变量解决问题。如:
0 * * * * . /etc/profile;/bin/sh /tmp/email.sh