使用 `at` 安排的命令失败但没有给出错误

使用 `at` 安排的命令失败但没有给出错误

最近学习了atUnix系统中的命令。

假设我想要一个 Python 脚本(可能会崩溃)在启动它 2 小时后运行。我会这样做:

$ echo python3 my_script.py | at now + 2hours 

我有几个问题:

  1. 这是正确的做法吗?
  2. 我如何捕获任何日志?因为如果脚本失败,at不会通知我,而且我无法访问错误。
  3. 我确实想使用它,at因为它让我的生活变得轻松很多,但它真的能做到这一点吗?我应该使用其他工具吗?

答案1

这是正确的做法吗?

是的,基本上at是正确的工具,即使你退出后它也可以工作


我如何才能捕获任何日志?

POSIX 规范at状态[强调我的]:

-m

at-job 运行完毕后,向调用用户发送邮件,宣布其完成。除非重定向到其他地方,否则 at-job 产生的标准输出和标准错误也应通过邮件发送给用户。即使作业没有产生任何输出,也应发送邮件。

如果-m未使用,则应通过邮件的方式向用户提供作业的标准输出和标准错误,除非它们被重定向到其他地方;如果没有这样的输出可提供,则实现不需要通知用户作业已完成。

确保您能够收到邮件,然后在时间到时检查邮件;或者重定向。

(“时间到了”的意思是“作业完成后”。根据您要运行的内容,您可能能够或不能提前知道需要多长时间。您的atwhen 调用at -l可能会(也可能不会)显示当前正在运行的作业。请阅读man 1 at。)

at使用“单独调用 shell”。您通过管道传送的命令at将被解释为 shell 代码。这意味着您可以在那里指定重定向。示例:

echo 'python3 my_script.py >>/path/to/log 2>&1' | at now + 2hours

另一个示例,这个示例记录实际命令之前和之后的时间戳,并使用此处的文档而不是echo

at now + 2hours <<EOF
{
echo start
date
python3 my_script.py
echo "$?"
date
echo finish
} >/path/to/log 2>&1
EOF

注意,如果重定向失败,则会向您发送一条说明此事实的错误消息(并且{ }不会执行其中的 shell 代码);因此,无论如何,接收和检查邮件都是有益的。


如果脚本失败,at不通知我

一般来说,这不是“如果作业失败”。at如果您不使用-m并且作业没有向 stdout(未重定向)和 stderr(未重定向)打印任何内容,则不会尝试通过邮件通知您。作业保持沉默才是最重要的,作业的退出状态无关紧要。

此外,没有报告该作业的退出状态(至少在我的 Debian 中)。

在上面的例子中,我包含了echo "$?"。这是获取 的退出状态的方法python3 my_script.py


at真的是要这么做吗?

是的,这种行为是设计使然。您可以称其为非最佳设计,但请注意,每个作业都在单独的进程组中运行,没有控制终端,因此即使您注销后它也可以工作;这有时是一个巨大的优势。邮件是联系当前可能已注销的用户的好方法。

尽管如此,因为你给予外壳代码at,您可以轻松传递多个命令并使用重定向,从而完全自定义在哪里打印什么,而不依赖邮件(在错误重定向的情况下邮件仍然有用)。


我应该使用其他工具吗?

这取决于你。在我看来,可能没有必要。at是一个完善的标准工具。也许存在一些限制,at这可能是想要一个更好的工具的理由,但所讨论的问题不应被视为其中之一,因为它可以轻松解决,如上所示。

此外,在 *nix 中,还有自定义命令的常规方法。例如:

  • alias at='at -m'会自动添加-matshell 中的命令。这样,您就可以at无条件地强制发送邮件,而无需-m每次都输入内容。

  • 这个 shell 函数:

    atl () (
    (printf "exec >'%s' 2>&1\n" "$LOG"; cat) | at "$@"
    )
    

    将允许您执行以下操作:

    echo python3 my_script.py | LOG=/path/to/log atl now + 2hours 
    

    上述函数是概念证明,它可能不安全。如果扩展$LOG包含单引号,则会出现错误。如果的值$LOG不完全受您控制,则可以执行不想要的(甚至是恶意的)代码。要轻松修复,请使用%q而不是'%s';xor(在 Bash 中)使用%s而不是'%s'and "${LOG@Q}" instead of"$LOG"`。但是这些方法不可移植。其他 shell 可能会为您提供类似的可能性。修改后的函数应该可以在 Bash 中工作:

    atl () {
    (printf "exec >%s 2>&1\n" "${LOG@Q}"; cat) | at "$@"
    }
    

有了这些知识,请自己回答“我应该使用其他工具吗?”。

相关内容