【问题标题】:Checking for duplicate cron jobs检查重复的 cron 作业
【发布时间】:2020-01-17 05:48:46
【问题描述】:

我们正在将代码部署到我们的应用程序服务器环境中,并且该过程的一部分是在服务器上创建许多 cron 作业。当代码被推送时,我们的部署脚本会使用以下内容毫无问题地创建所需的 cron 作业:

CRON_FILE=$SCRIPT_DIR/cron.txt 
if [[ ! -f "$CRON_FILE" ]]; then
        printf "Cron template file missing!\n\n"
        exit 1
fi
while read LINE || [[ -n "$LINE" ]]; do
        printf "\n> Adding cron job \"$LINE\"\n"
        crontab -l | { cat; echo "$LINE"; } | crontab -
done < $CRON_FILE

问题是在初始部署之后,其他部署会创建重复的 cron 作业。

关于如何检测 cron 作业是否已存在的任何指示?

【问题讨论】:

  • crontab -l | grep "${LINE}"?

标签: bash cron crontab


【解决方案1】:

添加 cron 作业时,请添加带有唯一标签的注释。稍后您可以使用该唯一标签来确定 cron 作业是否存在,也可以“卸载”该 cron 作业。

我一直这样做。我有一个可重用的脚本:

#!/bin/sh
#
# Usage: 
# 1. Put this script somewhere in your project
# 2. Edit "$0".crontab file, it should look like this, 
#    but without the # in front of the lines
#0  *   *   *   *   stuff_you_want_to_do
#15 */5 *   *   *   stuff_you_want_to_do
#*  *   1,2 *   *   and_so_on
# 3. To install the crontab, simply run the script
# 4. To remove the crontab, run ./crontab.sh --remove
# 

cd $(dirname "$0")

test "$1" = --remove && mode=remove || mode=add

cron_unique_label="# $PWD"

crontab="$0".crontab
crontab_bak=$crontab.bak
test -f $crontab || cp $crontab.sample $crontab

crontab_exists() {
    crontab -l 2>/dev/null | grep -x "$cron_unique_label" >/dev/null 2>/dev/null
}

# if crontab is executable
if type crontab >/dev/null 2>/dev/null; then
    if test $mode = add; then
        if ! crontab_exists; then
            crontab -l > $crontab_bak
            echo 'Appending to crontab:'
            cat $crontab
            crontab -l 2>/dev/null | { cat; echo; echo $cron_unique_label; cat $crontab; echo; } | crontab -
        else
            echo 'Crontab entry already exists, skipping ...'
            echo
        fi
        echo "To remove previously added crontab entry, run: $0 --remove"
        echo
    elif test $mode = remove; then
        if crontab_exists; then
            echo Removing crontab entry ...
            crontab -l 2>/dev/null | sed -e "\?^$cron_unique_label\$?,/^\$/ d" | crontab -
        else
            echo Crontab entry does not exist, nothing to do.
        fi
    fi
fi

在你的项目目录中将脚本保存为crontab.sh,并使用你的cron作业定义创建一个crontab.sh.crontab,例如:

0 0 * * * echo hello world
0 0 * * * date
  • 要安装您的 cron 作业,只需运行 ./crontab.sh
  • 脚本多次运行是安全的:它将检测唯一标签是否已经存在并跳过再次添加您的 cron 作业
  • 要卸载 cron 作业,请运行 ./crontab.sh --remove

我也把这个放到了 GitHub 上:https://github.com/janosgyerik/crontab-script

sed -e "\?^$cron_unique_label\$?,/^\$/ d"的解释:

  • 最简单的表达式基本上是:sed -e '/start/,/end/ d'
  • 意思是:删除匹配开始模式和结束模式的行之间的内容,包括包含这些模式的行
  • 脚本用双引号而不是单引号来引用sed命令,因为它需要扩展$cron_unique_label shell变量的值
  • 起始模式\?^$cron_unique_label\$?使用一对?而不是/来包围模式,因为$cron_unique_label包含/,这会导致问题
  • 开头的? 必须用反斜杠转义,但老实说我不知道​​为什么。
  • ^匹配行首和$行尾,$必须转义,否则shell会扩展$?shell变量的值
  • 结束模式/^\$/比较简单,它匹配行首后跟行尾,也就是空行,同样$必须被转义
  • 末尾的dsed命令,删除匹配的行,有效地将其从crontab -l的内容中删除,我们可以通过管道传递给crontab -

【讨论】:

  • 嗯,crontab -l 2&gt;/dev/null | sed -e "\?^$cron_unique_label\$?,/^\$/ d" | crontab - 似乎没有删除 cron 作业 - 运行 crontab -l 2&gt;/dev/null | sed -e "\?^$cron_unique_label\$?,/^\$/ d" 显示所有具有正确唯一标签的匹配作业,但最终管道似乎没有删除条目。
  • 抱歉,无法编辑上述评论 - 意思是说,这里的 ^ 应该否定唯一标签,但似乎没有这样做。
  • ^ 不是用于否定,而是用于行首。该模式从唯一的行开始匹配直到一个空行,并将其删除,从而从crontab -l 的当前内容中过滤掉。我修改了我的帖子,详细解释了它是如何工作的。那里的每个符号都是有目的的,它应该起作用。如果您还需要帮助,请告诉我!
  • 谢谢。删除和添加就像一个魅力。尽管之后可能使用它来清理 crontab:crontab -l | sed '/^$/d' | crontab -
【解决方案2】:

奇怪但非常彻底的答案。 IMO 过于复杂。

对于任何来此寻求 2017 年答案的人来说,这是一条不错的路线:

crontab -l | grep 'match-your-cronjob-search' || (crontab -l 2>/dev/null; echo "* * * * * /bin/cronjobCommandYouWant >> /dev/null 2>&1") | crontab -

对我们来说非常有用,不会有重复的 crons。

这是原始海报中经过编辑的脚本:

    CRON_FILE=$SCRIPT_DIR/cron.txt 
if [[ ! -f "$CRON_FILE" ]]; then
        printf "Cron template file missing!\n\n"
        exit 1
fi
while read LINE || [[ -n "$LINE" ]]; do
        printf "\n> Adding cron job \"$LINE\"\n"
        crontab -l | grep "$LINE" || (crontab -l 2>/dev/null; echo "$LINE") | crontab -
done < $CRON_FILE

【讨论】:

  • 喜欢这种简单的方法。我所做的唯一添加是在 grep 之后添加 ` > /dev/null 2>&1` 以抑制输出。
【解决方案3】:

您的脚本 janos 很棒,运行良好,并且正是我正在寻找的一个小故障。 我无法管理多个 xxx.crontab 模板。 您的脚本运行良好,我将它添加到我的引导例程中并稍作修改,这样我就可以传递 xx.crontab 文件名的第一个参数 $1 和第二个参数 $2 可以删除。 我有父 shell 脚本,然后有条件地决定,我想添加/删除哪些,所有或组合的 crontab 文件。

这里包含我修改的脚本:

#!/bin/sh
#
# Usage:
# 1. Put this script somewhere in your project
# 2. Edit "$1".crontab file, it should look like this,
#    but without the # in front of the lines
#0  *   *   *   *   stuff_you_want_to_do
#15 */5 *   *   *   stuff_you_want_to_do
#*  *   1,2 *   *   and_so_on
# 3. To install the crontab, run ./crontab.sh <nameOfCronTabfile>
# 4. To remove the crontab, run ./crontab.sh <nameOfCronTabfile> --remove

cd $(dirname "$0")

test "$2" = --remove && mode=remove || mode=add

cron_unique_label="# cmID:$PWD|$1#"

crontab="$1".crontab
crontab_bak=$crontab.bak
test -f $crontab || cp $crontab.sample $crontab

crontab_exists() {
    crontab -l 2>/dev/null | grep -x "$cron_unique_label" >/dev/null 2>/dev/null
}

# if crontab is executable
if type crontab >/dev/null 2>/dev/null; then
    if test $mode = add; then
        if ! crontab_exists; then
            crontab -l > $crontab_bak
            echo 'Appending to crontab:'
            echo '-----------------------------------------------'
            cat $crontab
            crontab -l 2>/dev/null | { cat; echo; echo $cron_unique_label; cat $crontab; echo "# cm #"; } | crontab -
        else
            echo 'Crontab entry already exists, skipping ...'
            echo
        fi
        echo '-----------------------------------------------'
        echo "To remove previously added crontab entry, run: $0 $1 --remove"
        echo
    elif test $mode = remove; then
        if crontab_exists; then
            echo 'Removing crontab entry ...'
            crontab -l 2>/dev/null | sed -e "\?^$cron_unique_label\$?,/^# cm #\$/ d" | crontab -
        else
            echo 'Crontab entry does not exist, nothing to do.'
        fi
    fi
fi

更新: 抱歉,没有注意到用于删除 crontab 的弱空行模式。只需在找到 crontab id 后删除所有内容,包括手动添加的 crontab。 将空行结束模式更改为小结束标记。 所以它会添加 crontabs:

crontab -l 2>/dev/null | { cat; echo; echo $cron_unique_label; cat $crontab; echo "# cm #"; } | crontab -

.. 并仅删除此 cron:

crontab -l 2>/dev/null | sed -e "\?^$cron_unique_label\$?,/^# cm #\$/ d" | crontab -

【讨论】:

    【解决方案4】:

    我们也可以在代码中做到这一点: 首先在 bash 中,查看手册:man 2 open:

    int fd = open('/tmp/1.txt', O_CREAT|O_EXCL)
    if (fd === -1) {
       print "job exist"
       exit(1)
    }
    //unlink()
    // your code
    

    js版:

    const fs = require('fs')
    try {
    const fd = fs.openSync('/tmp/1.txt', 'wx')
    } catch(err) {
      console.log('job exist')
      process.exit(0)
    }
    
    fs.unlinkSync('/tmp/1.txt')
    fs.appendFileSync(fd, 'pid', 'utf8');
    

    【讨论】:

      猜你喜欢
      • 2013-07-18
      • 1970-01-01
      • 2013-04-28
      • 2012-03-21
      • 1970-01-01
      • 2018-04-18
      • 1970-01-01
      • 2019-06-05
      • 2013-08-20
      相关资源
      最近更新 更多