【问题标题】:How to simulate the environment cron executes a script with?如何模拟环境 cron 执行脚本?
【发布时间】:2011-01-09 06:42:26
【问题描述】:

我通常对 cron 如何执行脚本有几个问题,因为它们通常没有我的环境设置。有没有办法以与 cron 相同的方式调用 bash(?) 以便我可以在安装脚本之前对其进行测试?

【问题讨论】:

标签: bash scripting cron


【解决方案1】:

将此添加到您的 crontab(暂时):

* * * * * env > ~/cronenv

运行后,执行以下操作:

env - `cat ~/cronenv` /bin/sh

这假设你的 cron 运行 /bin/sh,这是默认的,不管用户的默认 shell。

脚注:如果env 包含更高级的配置,例如PS1=$(__git_ps1 " (%s)")$,它会隐秘地出错env: ": No such file or directory

【讨论】:

  • 注意:如果将其添加到全局 /etc/crontab,您也需要用户名。例如。 * * * * * 根环境 > ~/cronenv
  • 好,简单的想法。对于不耐烦的人使用'* * * * *'在下一分钟运行,并记住在玩完后再次关闭;-)
  • @Madsn 要返回上一个 bash shell 尝试:exit
  • 我将它保存到 /tmp/cronenv 以避免混淆 ~ 在该上下文中的含义。
  • 这个答案的重要性不容小觑。值得在一本书中包含一个段落。
【解决方案2】:

Cron 默认只提供这个环境:

  • HOME用户主目录
  • LOGNAME用户登录
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

如果您需要更多,您可以在 crontab 中的调度表之前定义您的环境的脚本。

【讨论】:

【解决方案3】:

几种方法:

  1. 导出 cron 环境并获取它:

    添加

    * * * * * env > ~/cronenv
    

    到你的 crontab,让它运行一次,关闭它,然后运行

    env - `cat ~/cronenv` /bin/sh
    

    你现在在一个 sh 会话中,它有 cron 的环境

  2. 将您的环境带入 cron

    您可以跳过上面的练习,只需在您的 cron 作业前执行 . ~/.profile,例如

    * * * * * . ~/.profile; your_command
    
  3. 使用屏幕

    以上两种解决方案仍然失败,因为它们提供了一个连接到正在运行的 X 会话的环境,可以访问 dbus 等。例如,在 Ubuntu 上,nmcli(网络管理器)将在上述两种方法中工作,但是在 cron 中仍然失败。

    * * * * * /usr/bin/screen -dm
    

    将上面的行添加到 cron,让它运行一次,然后将其关闭。连接到您的屏幕会话 (screen -r)。如果您正在检查屏幕会话是否已创建(使用ps),请注意它们有时是大写的(例如ps | grep SCREEN

    现在即使nmcli 和类似的也会失败。

【讨论】:

  • 我对 Option1 的首选语法是 env -i $(cat ~/cronenv) /bin/sh-i- (--ignore-environment) 相同,但不那么神秘。 $(...)`...` 相同
【解决方案4】:

你可以运行:

env - your_command arguments

这将在空环境下运行 your_command。

【讨论】:

  • cron 不会在完全空的环境中运行,是吗?
  • gregseth 通过 cron 识别了环境中包含的变量。您可以在命令行中包含该变量。 $ env - PATH="$PATH" 命令参数
  • @DragonFax @dimba 我使用env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command arguments 这似乎可以解决问题
  • 这是在“敌对”或未知环境中测试脚本的一种非常简单的方法。如果你足够明确,它会在这个中运行,它会在 cron 下运行。
【解决方案5】:

取决于帐户的外壳

sudo su
env -i /bin/sh

sudo su
env -i /bin/bash --noprofile --norc

来自http://matthew.mceachen.us/blog/howto-simulate-the-cron-environment-1018.html

【讨论】:

    【解决方案6】:

    六年后的回答:环境不匹配问题是systemd“timers”作为cron替代解决的问题之一。无论您是从 CLI 还是通过 cron 运行 systemd“服务”,它都会收到完全相同的环境,避免了环境不匹配问题。

    手动通过时导致 cron 作业失败的最常见问题是 cron 设置的限制性默认 $PATH,这在 Ubuntu 16.04 上是这样的:

    "/usr/bin:/bin"
    

    相比之下,Ubuntu 16.04 上systemd 设置的默认$PATH 是:

    "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    

    因此,systemd 计时器找到二进制文件的可能性已经很大。

    systemd 计时器的缺点是设置它们的时间稍长。您首先创建一个“服务”文件来定义您要运行的内容,并创建一个“计时器”文件来定义运行它的时间表,最后“启用”计时器来激活它。

    【讨论】:

      【解决方案7】:

      创建一个运行 env 并将 stdout 重定向到文件的 cron 作业。 使用“env -”旁边的文件来创建与 cron 作业相同的环境。

      【讨论】:

      • 对不起,这让我感到困惑。 “env - script”还不够吗?
      • 这会给你一个空的环境。通过 cron 运行脚本时,环境不为空。
      【解决方案8】:

      不要忘记,由于 cron 的父级是 init,它在没有控制终端的情况下运行程序。您可以使用这样的工具进行模拟:

      http://libslack.org/daemon/

      【讨论】:

        【解决方案9】:

        默认情况下,cron 使用您的系统对sh 的想法执行其作业。这可能是实际的 Bourne shell 或 dashashkshbash(或另一个)符号链接到 sh(因此在 POSIX 模式下运行)。

        最好的办法是确保你的脚本有他们需要的东西,并假设没有为他们提供任何东西。因此,您应该使用完整的目录规范并自己设置环境变量,例如$PATH

        【讨论】:

        • 这正是我要解决的问题。我们会遇到大量错误假设某些内容的脚本问题。做完整的路径和设置环境变量,所有的垃圾都以可怕的巨大的不可维护的 cron 行结束
        • re *sh 抱歉,我是在 bash = shell 的陪伴下长大的,所以我很难记住替代(有时更好)的 shell。
        • @Jorge:crontab 中的行应该相当短。您应该在脚本(或包装脚本)中完成所有需要的设置。以下是来自 crontab 的典型行作为示例:0 0 * * 1 /path/to/executable >/dev/null 2>&1,然后,在“可执行文件”中,我将为 $PATH 等设置值,并使用完整目录规范来输入和输出文件等。例如:@ 987654331@
        【解决方案10】:

        我发现的另一种简单方法(但可能容易出错,我仍在测试中)是在执行命令之前获取用户的配置文件。

        编辑 /etc/cron.d/ 脚本:

        * * * * * user1 comand-that-needs-env-vars
        

        会变成:

        * * * * * user1 source ~/.bash_profile; source ~/.bashrc; comand-that-needs-env-vars
        

        很脏,但它为我完成了工作。有没有办法模拟登录?只是一个可以运行的命令? bash --login 没用。听起来那会是更好的方法。

        编辑:这似乎是一个可靠的解决方案:http://www.epicserve.com/blog/2012/feb/7/my-notes-cron-directory-etccrond-ubuntu-1110/

        * * * * * root su --session-command="comand-that-needs-env-vars" user1 -l
        

        【讨论】:

          【解决方案11】:

          接受的答案确实提供了一种使用 cron 将使用的环境运行脚本的方法。正如其他人指出的那样,这不是调试 cron 作业的唯一需要标准。

          确实,cron 也使用非交互式终端,没有附加输入等。

          如果有帮助,我已经编写了一个脚本,可以像 cron 一样轻松运行命令/脚本。用你的命令/脚本作为第一个参数调用它,你很好。

          此脚本也在Github 上托管(并可能更新)。

          #!/bin/bash
          # Run as if it was called from cron, that is to say:
          #  * with a modified environment
          #  * with a specific shell, which may or may not be bash
          #  * without an attached input terminal
          #  * in a non-interactive shell
          
          function usage(){
              echo "$0 - Run a script or a command as it would be in a cron job, then display its output"
              echo "Usage:"
              echo "   $0 [command | script]"
          }
          
          if [ "$1" == "-h" -o "$1" == "--help" ]; then
              usage
              exit 0
          fi
          
          if [ $(whoami) != "root" ]; then
              echo "Only root is supported at the moment"
              exit 1
          fi
          
          # This file should contain the cron environment.
          cron_env="/root/cron-env"
          if [ ! -f "$cron_env" ]; then
              echo "Unable to find $cron_env"
              echo "To generate it, run \"/usr/bin/env > /root/cron-env\" as a cron job"
              exit 0
          fi
          
          # It will be a nightmare to expand "$@" inside a shell -c argument.
          # Let's rather generate a string where we manually expand-and-quote the arguments
          env_string="/usr/bin/env -i "
          for envi in $(cat "$cron_env"); do
             env_string="${env_string} $envi "
          done
          
          cmd_string=""
          for arg in "$@"; do
              cmd_string="${cmd_string} \"${arg}\" "
          done
          
          # Which shell should we use?
          the_shell=$(grep -E "^SHELL=" /root/cron-env | sed 's/SHELL=//')
          echo "Running with $the_shell the following command: $cmd_string"
          
          
          # Let's route the output in a file
          # and do not provide any input (so that the command is executed without an attached terminal)
          so=$(mktemp "/tmp/fakecron.out.XXXX")
          se=$(mktemp "/tmp/fakecron.err.XXXX")
          "$the_shell" -c "$env_string $cmd_string" >"$so" 2>"$se" < /dev/null
          
          echo -e "Done. Here is \033[1mstdout\033[0m:"
          cat "$so"
          echo -e "Done. Here is \033[1mstderr\033[0m:"
          cat "$se"
          rm "$so" "$se"
          

          【讨论】:

            【解决方案12】:

            Answer https://stackoverflow.com/a/2546509/5593430 展示了如何获取 cron 环境并将其用于您的脚本。但请注意,环境可能因您使用的 crontab 文件而异。我创建了三个不同的 cron 条目来通过 env &gt; log 保存环境。这些是在 Amazon Linux 4.4.35-33.55.amzn1.x86_64 上的结果。

            1。全局 /etc/crontab 与 root 用户

            MAILTO=root
            SHELL=/bin/bash
            USER=root
            PATH=/sbin:/bin:/usr/sbin:/usr/bin
            PWD=/
            LANG=en_US.UTF-8
            SHLVL=1
            HOME=/
            LOGNAME=root
            _=/bin/env
            

            2。 root的用户crontab (crontab -e)

            SHELL=/bin/sh
            USER=root
            PATH=/usr/bin:/bin
            PWD=/root
            LANG=en_US.UTF-8
            SHLVL=1
            HOME=/root
            LOGNAME=root
            _=/usr/bin/env
            

            3。 /etc/cron.hourly/中的脚本

            MAILTO=root
            SHELL=/bin/bash
            USER=root
            PATH=/sbin:/bin:/usr/sbin:/usr/bin
            _=/bin/env
            PWD=/
            LANG=en_US.UTF-8
            SHLVL=3
            HOME=/
            LOGNAME=root
            

            最重要的是PATHPWDHOME 不同。确保在您的 cron 脚本中设置这些以依赖于稳定的环境。

            【讨论】:

              【解决方案13】:

              在我的例子中,cron 正在使用 sh 执行我的脚本,但它无法执行某些 bash 语法。 在我的脚本中,我添加了环境变量SHELL:

              #!/bin/bash
              SHELL=/bin/bash
              

              【讨论】:

                【解决方案14】:

                我不相信有;我知道测试 cron 作业的唯一方法是将其设置为在未来运行一两分钟,然后等待。

                【讨论】:

                • 这就是我要模拟的原因
                猜你喜欢
                • 2011-06-17
                • 1970-01-01
                • 1970-01-01
                • 2013-02-05
                • 2017-06-26
                • 1970-01-01
                • 2017-06-09
                • 2018-02-07
                • 2020-10-04
                相关资源
                最近更新 更多