您可以使用“at”命令执行 Bash 函数吗?

您可以使用“at”命令执行 Bash 函数吗?

我想在预定的时间执行 Bash 函数。我认为正确的工具是at命令。

但是,它似乎不起作用。

function stupid () {
    date
}

export -f stupid

cat << EOM | at now + 1 minute
stupid
EOM

等了一分钟后,我检查了我的邮件,这是我看到的错误:

sh: line 41: syntax error near unexpected token `=\(\)\ {\ \ date"
"}'
sh: line 41: `"}; export BASH_FUNC_stupid()'

我不明白这里出了什么问题,特别是因为我知道该功能有效。

$ stupid
Fri May 29 21:05:38 UTC 2015

查看错误,我认为使用了不正确的 shell 来执行该函数(sh而不是bash),但如果我检查,$SHELL我会看到它指向/bin/bash,并且手册页at显示:

$ echo $SHELL
/bin/bash
$ man at

    ...

    SHELL   The value of the SHELL environment variable at the time
           of at invocation will determine which shell is used  to
           execute  the at job commands.

所以 Bash 应该是运行我的函数的 shell。

这是怎么回事?有没有办法让我的 Bash 函数一起运行at

答案1

Bash 函数通过环境导出。该at命令通过生成重现环境的 shell 代码,使环境、umask 和调用进程的当前目录可供脚本使用。你的 at 工作执行的脚本是这样的:

#!/bin/bash
umask 022
cd /home/nick
PATH=/usr/local/bin:/usr/bin:/bin; export PATH
HOME=/home/nick; export HOME
stupid

在旧版本的 bash 中,函数被导出为一个变量,其中包含函数名称和()以定义函数的代码开头并由其组成的值,例如

stupid="() {
    date
}"; export stupid

这使得许多场景容易受到安全漏洞的影响,炮弹休克虫(发现于斯蒂芬·查泽拉斯),它允许任何人以任何名称注入环境变量的内容,以在 bash 脚本中执行任意代码。带有 Shellshock 修复的 bash 版本使用不同的方式:它们将函数定义存储在一个变量中,该变量的名称包含在环境变量中找不到的字符,并且 shell 不会将其解析为赋值。

BASH_FUNC_stupid%%="() {
    date
}"; export stupid

由于%,这不是有效的 sh 语法,即使在 bash 中也是如此,因此 at 作业会失败,无论它是否尝试使用该函数。这Debian 版本的 at,在许多 Linux 发行版中使用,是改变了在版本 3.16 中,仅导出在 shell 脚本中具有有效名称的变量。因此,较新版本的 at 不会通过 Shellshock 后的 bash 导出函数,而较旧版本则会出错。

即使使用 Shellshock 之前的 bash 版本,导出的函数也只能在从 at 作业启动的 bash 脚本中起作用,而不能在 at 作业本身中起作用。在 at 作业本身中,即使它是由 bash 执行的,stupid也只是另一个环境变量; bash 仅在启动时导入函数。

要将函数导出到 at 作业(无论 bash 或 at 版本如何),请将它们放入一个文件中并从您的作业中获取该文件,或者将它们直接包含在您的作业中。要以可读回的格式打印所有定义的函数,请使用declare -f.

{ declare -f; cat << EOM; } | at now + 1 minute
stupid
EOM

答案2

at被设计为使用/bin/sh而不是/bin/bash,因此它会在前面添加一个您可以使用 看到的代码序言。如果你想插入东西并使用它,这里有一个简单的函数:at -c {jobnumber}bash

at_with_bash_alias_and_functions(){
  ( 
    echo "exec /bin/bash <<END"
    alias
    typeset -f
    cat
    echo END
  ) | sed 's/\$/\\$/g' | at "$@"
}

显示别名和函数是否存在:

at_with_bash_alias_and_functions now <<< "alias;typeset -f"

限制:内联可能会由于 shell 替换而丢失语法,它的工作方式与at.使用exec cat而不是验证您的语法exec bash

答案3

好吧,您已经知道您会收到邮件。这很酷,因为你绝对bash邮件到达时执行函数。

这是来自man bash

  • $MAILPATH

    • 要检查邮件的以冒号分隔的文件名列表。当邮件到达特定文件时要打印的消息可以通过使用分隔符将文件名与消息分开来指定。当在消息文本中使用时,$_扩展为当前邮件文件的名称。例子:

      MAILPATH='/var/mail/bfox?"You have mail":~/shell-mail?"$_ has mail!"'
      
    • bash为此变量提供默认值,但它使用的用户邮件文件的位置取决于系统(例如,/var/mail/$USER

顺便说一句,$_不是仅有的在该上下文中可能发生的扩展 - 您几乎可以扩展任何内容,包括命令替换。事实上,你不一定需要打印任何事情。并且文件不一定需要包含邮件$MAILCHECK或者 - 当每秒执行一次检查表明文件自上次查看以来已被修改时,shell 将尝试发出通知。

尽管如此,shell 处理的方式$MAILxxxx通常依赖于提示 - 也就是说,无论 的$MAILCHECK值如何,如果您没有在正确的时间拉出新的提示,您都不会收到通知 - 并且直到您触发时才会收到通知下一个提示。我对此不确定,但它可能set -b会影响这一点。

无论如何,做这种事情的方法不止一种。也许最明显的是使用信号 - 安排at工作,例如:

echo kill -USR1 "$$" | at now + 1 minute
trap some_func USR1

看看会发生什么。

相关内容