如何通过脚本添加/删除 cron 作业?

如何通过脚本添加/删除 cron 作业?

我将制作一个在启动时执行并定期运行的 bash 脚本。

我希望它是用户可配置的,以便用户可以0 * * * * my_script通过运行添加 cron 作业my_script add 0 * * * *,通过 列出作业my_script list,并通过命令my_script remove job_number输出中列出作业编号的位置删除作业my_script list

如果我可以单独管理 crontab 文件,这将很容易实现。然而,似乎 crontab 对于每个用户来说只有一个文件(如果不是,请告诉我)。当然,直接处理该 crontab 文件是一个糟糕的解决方案。

那么处理 cron 作业的正确方法是什么?或者,是否有更好的方法来处理定期运行的脚本?

状况:

  1. 任何用户都应该能够运行它,无论是否有特权。
  2. 没有依赖性。

附加问题:

由于我找不到任何适当的方法来管理定期运行的脚本,我想我可能做错了什么。从软件设计的意义上来说,实现一个接口来管理软件的定时任务是不是不切实际?我应该将所有日程管理留给用户吗?

答案1

在大多数 Unix 系统上,使用 cron 是安排任务定期运行的正确方法。使用个人 crontab 是用户安排自己的任务的最方便的方法。系统任务可以由 root 调度(不使用下面的脚本!)在系统 crontab 中,其格式通常略有不同(带有用户名的额外字段)。

这是给您的一个简单脚本。任何用户都可以使用它来管理自己的个人 crontab。

  • 它不会对其输入进行任何类型的验证,除非您给它的参数太少,它会抱怨。因此,完全有可能添加格式不正确的 crontab 条目。

  • remove子命令采用行号,并将删除 crontab 中该行上的内容,无论它是什么。该号码未经消毒直接传递至sed

  • 当您添加一个 crontab 条目时,必须用引号引起来。这会影响您处理报价的方式里面crontab 条目本身。

大多数问题对您来说应该相对容易解决。

#!/bin/sh

usage () {
    cat <<USAGE_END
Usage:
    $0 add "job-spec"
    $0 list
    $0 remove "job-spec-lineno"
USAGE_END
}

if [ -z "$1" ]; then
    usage >&2
    exit 1
fi

case "$1" in
    add)
        if [ -z "$2" ]; then
            usage >&2
            exit 1
        fi

        tmpfile=$(mktemp)

        crontab -l >"$tmpfile"
        printf '%s\n' "$2" >>"$tmpfile"
        crontab "$tmpfile" && rm -f "$tmpfile"
        ;;
    list)
        crontab -l | cat -n
        ;;
    remove)
        if [ -z "$2" ]; then
            usage >&2
            exit 1
        fi

        tmpfile=$(mktemp)

        crontab -l | sed -e "$2d" >"$tmpfile"
        crontab "$tmpfile" && rm -f "$tmpfile"
        ;;
    *)
        usage >&2
        exit 1
esac

使用示例:

$ ./script
Usage:
    ./script add "job-spec"
    ./script list
    ./script remove "job-spec-lineno"

$ ./script list
     1  */15 * * * * /bin/date >>"$HOME"/.fetchmail.log
     2  @hourly /usr/bin/newsyslog -r -f "$HOME/.newsyslog.conf"
     3  @reboot /usr/local/bin/fetchmail

$ ./script add "0 15 * * * echo 'hello world!'"

$ ./script list
     1  */15 * * * * /bin/date >>"$HOME"/.fetchmail.log
     2  @hourly /usr/bin/newsyslog -r -f "$HOME/.newsyslog.conf"
     3  @reboot /usr/local/bin/fetchmail
     4  0 15 * * * echo 'hello world!'

$ ./script remove 4

$ ./script list
     1  */15 * * * * /bin/date >>"$HOME"/.fetchmail.log
     2  @hourly /usr/bin/newsyslog -r -f "$HOME/.newsyslog.conf"
     3  @reboot /usr/local/bin/fetchmail

答案2

您当前的 cron 实现可能支持 /etc/cron.d,其中作业在常规时间字段之后和命令之前有一个额外的“运行身份”用户指定。因此,只需调整您的界面以在该目录中创建文件,就像常规 cron 条目一样,并在第五个字段之后添加用户名(可能从登录用户名中提取)。然后您可以为每个文件执行一项作业。 :)

看着man 5 crontab https://linux.die.net/man/5/crontab

请注意,这样做的主要问题是,可以编辑该目录中文件的用户可以创建一个以 root 身份运行的文件。因此,您可能需要一个通过 sudo 运行的包装脚本,它将验证输入并将计算出的用户名强制写入生成的文件中。然后需要确保您在脚本中安全地执行操作,例如不信任 $PATH 等。

PS,在shell脚本中:getent passwd $(</proc/self/loginuid)

老实说,如果您要让用户了解 cron 时间格式,那么多花几秒钟来教他们使用crontab -e$EDITOR如果 vi 太可怕的话还需要设置)并不是非常困难。

答案3

如果你想要一个 cron 条目:

0 * * * * my_script

那么我建议您为 cron 管理函数考虑一个单独的名称,例如“cron_mgmt”,其第一个参数是 CASE 语句中的 SWITCH:

cron_mgmt () {

    case $1 in
    add ) ... ;;
    list ) ...;;
    remove ) ...
    help ) ...
    * )  echo "You need help. Here it is"
         cron_mgmt help
         ;;
    esac
}   

答案4

不要推出自己的界面,而是保持简单。要安装具有特定计划的 cron 作业,请将其放置在特定目录中。要删除它,请将其从目录中删除。您不需要为用户提供任何工具来执行此操作,只需告诉他们将作业放入哪个目录即可。

这就是大多数 Linux 发行版管理可由各个软件包安装的系统 cron 作业的方式。 Debian、Fedora、Arch Linux 和其他操作系统都提供了一个名为 的工具,run-parts其工作是逐个运行特定目录中的脚本。不同的实现run-parts对于脚本可接受的文件名有不同的规则。如果您不想依赖于此,您可以自己推出:

for x in /path/to/daily-jobs/*; do
  if [ -x "$x" ]; then
    case "${x##*/}" in
      *[!-0-9A-Z_a-z]*) :;; # skip file names containing "weird" characters
      *) "$x";;
    esac
  fi
done

如果你想让用户指定他们自己的时间表,那么这种方法就行不通。每次提交新作业时都需要重建 crontab 文件。这是可以做到的,但可靠地设置起来很复杂。但是,如果用户可以指定自己的计划,那么就没有理由在 之上添加另一个界面,或者在后台crontab -e调用的 GUI 。crontab -e

相关内容