Bash 脚本手动运行,但在 crontab 上失败

Posted

技术标签:

【中文标题】Bash 脚本手动运行,但在 crontab 上失败【英文标题】:Bash script runs manually, but fails on crontab 【发布时间】:2013-01-14 18:24:32 【问题描述】:

我是 shell 脚本的新手。我编写了一个shell脚本来对mysql数据库进行增量备份。该脚本是可执行格式,手动执行时运行成功,但通过crontab执行时失败。

crontab 入口是这样的:

*/1 * * * * /home/db-backup/mysqlbackup.sh

下面是shell脚本代码-

#!/bin/sh
MyUSER="root"       # USERNAME
MyPASS="password"         # PASSWORD
MyHOST="localhost"  # Hostname
Password="" #Linux Password

MYSQL="$(which mysql)"
if [ -z "$MYSQL" ]; then
echo "Error: MYSQL not found"
exit 1
fi
MYSQLADMIN="$(which mysqladmin)"
if [ -z "$MYSQLADMIN" ]; then
    echo "Error: MYSQLADMIN not found"
    exit 1
fi
CHOWN="$(which chown)"
if [ -z "$CHOWN" ]; then
    echo "Error: CHOWN not found"
    exit 1
fi
CHMOD="$(which chmod)"
if [ -z "$CHMOD" ]; then
    echo "Error: CHMOD not found"
    exit 1
fi

GZIP="$(which gzip)"
if [ -z "$GZIP" ]; then
    echo "Error: GZIP not found"
    exit 1
fi
CP="$(which cp)"
if [ -z "$CP" ]; then
    echo "Error: CP not found"
    exit 1
fi
MV="$(which mv)"
if [ -z "$MV" ]; then
    echo "Error: MV not found"
    exit 1
fi
RM="$(which rm)"
if [ -z "$RM" ]; then
    echo "Error: RM not found"
    exit 1
fi
RSYNC="$(which rsync)"
if [ -z "$RSYNC" ]; then
    echo "Error: RSYNC not found"
    exit 1
fi

MYSQLBINLOG="$(which mysqlbinlog)"
if [ -z "$MYSQLBINLOG" ]; then
    echo "Error: MYSQLBINLOG not found"
    exit 1
fi
# Get data in dd-mm-yyyy format
NOW="$(date +"%d-%m-%Y-%T")"

DEST="/home/db-backup"
mkdir $DEST/Increment_backup.$NOW
LATEST=$DEST/Increment_backup.$NOW
$MYSQLADMIN -u$MyUSER -p$MyPASS flush-logs
newestlog=`ls -d /usr/local/mysql/data/mysql-bin.?????? | sed 's/^.*\.//' | sort -g | tail -n 1`
echo $newestlog
for file in `ls /usr/local/mysql/data/mysql-bin.??????`
do
        if [ "/usr/local/mysql/data/mysql-bin.$newestlog" != "$file" ]; then
     echo $file             
     $CP "$file" $LATEST         
        fi
done
for file1 in `ls $LATEST/mysql-bin.??????`
do
 $MYSQLBINLOG $file1>$file1.$NOW.sql 
 $GZIP -9 "$file1.$NOW.sql"     
 $RM "$file1"
done
$RSYNC -avz $LATEST /home/rsync-back
首先,在 crontab 上安排时,它没有显示任何错误。如何知道脚本是否正在运行? 其次,在 crontab 中执行 shell 脚本的正确方法是什么。 一些博客建议更改环境变量。最好的解决方案是什么

当我执行 $echo PATH 时,我得到了这个

/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/mysql/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/platform-tools:~/usr/lib/jvm/jdk-6/bin

【问题讨论】:

这不是要回答问题本身,而是建议查看 mysql 备份脚本的 3rd-party 工具,并为自己省去一些麻烦。这是很常见的事情,所以有很多可用的工具。我用过的一种叫做automysqlbackup。我认为这使用了转储方法而不是直接使用数据文件,因此它可能不适合您,但可能还有其他人会这样做。 顺便说一句——MYSQL="$(which mysql)" 通常不是你应该遵循的做法。 shell 已经缓存了 PATH 查找结果,并且比启动外部程序(如 which)更有效。 【参考方案1】:

问题可能是您在手动环境中的 $PATH 与运行 crontab 的环境不同。因此,which 找不到您的可执行文件。要解决此问题,首先在手动环境中打印您的路径 (echo $PATH),然后在您在 crontab 中运行的脚本顶部手动设置 PATH。或者只是通过完整路径引用程序。

编辑:将其添加到脚本顶部附近,在所有 which 调用之前:

export PATH="/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/mysql/bin:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/platform-tools:~/usr/lib/jvm/jdk-6/bin"

【讨论】:

最好执行在 bash 中设置环境的配置文件,而不是手动复制它。这里有一个描述:linuxfromscratch.org/blfs/view/6.3/postlfs/profile.html 最好在尽可能少的环境下执行 cron 作业,并精确指定以满足作业的需要。执行您的配置文件会引入过多的设置和不相关的设置,这意味着您可能会通过更改交互式环境意外地破坏您的 cron 作业。对于像这样的 mysql dump 它不应该在 your 帐户下执行,它应该在 mysql 用户 crontab 下运行;您也不应该以 mysql 用户身份登录,因此该用户的 PATH 是未知的(系统默认值)。 @Jay - Stephen P 是绝对正确的——最好的方法是使用显式路径,它是轻量级和直接的,而不是拉入整个配置文件,这是复杂和过度的。在我看来,您的建议完全属于“不良做法”阵营。【参考方案2】:

另一种更通用的方法是让 cron 运行用户的 bash 登录进程。除了 PATH,这还将获取任何 LD_LIBRARY_PATH、LANG 设置、其他环境变量等。为此,请将您的 crontab 条目编码为:

34  12  * * *   bash -l /home/db-backup/mysqlbackup.sh

【讨论】:

不会触发之前提到的同样的问题吗?即使不需要,所有 .bash_profile 和 .bash_rc 也会运行?我问是因为我面临同样的问题,我喜欢使用 BASH_ENV 指向一个最小的 env 脚本,但是当通过 crontab 运行 shell 时这不起作用。【参考方案3】:

我的问题是我在 /etc/cron.d (Centos 7) 中设置了 cron 作业。似乎这样做时我需要指定执行脚本的用户,这与在用户级别输入 cronjob 不同。

我所要做的就是

*/1 * * * * root perl /path/to/my/script.sh
*/5 * * * * root php /path/to/my/script.php

“root”表示我以 root 身份运行脚本。 还需要确保在文件顶部定义了以下内容。你的路径可能不同。如果您不确定,请尝试“which perl”、“which php”命令。

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin

【讨论】:

【参考方案4】:

只需在脚本开头导入您的用户配置文件即可。

即:

. /home/user/.profile

【讨论】:

这对我来说还不够 不是一个好主意。这会创建对 .profile 所做的一切的依赖。只要 .profile 只输出一个字符,您就会收到一封来自 cron 的邮件。【参考方案5】:

    在您的 crontab 文件中,您的脚本条目可以包含

    * * * * * /bin/bash /path/to/your/script
    

    确保在脚本中使用完整路径。我看过它,但我没有看到任何东西,以防万一我错过了。

【讨论】:

以上是关于Bash 脚本手动运行,但在 crontab 上失败的主要内容,如果未能解决你的问题,请参考以下文章

crontab 和手动运行脚本的结果不同

crontab不执行sh脚本 手动可以执行crontab执行失败【亲测】

如何使用 crontab 运行运行 Python 脚本的 Bash 脚本

该脚本从命令行运行,但 crontab 失败

当它从命令行 bash 工作时,无法从 crontab 运行 bash 脚本

bash 脚本不能通过 crontab 工作