在 shell 脚本中,如何安排函数在指定时间运行?

在 shell 脚本中,如何安排函数在指定时间运行?

在 bash 脚本中,我有一些需要在执行脚本时运行的代码,然后是我想要在特定时间执行的函数。我怎样才能做到这一点?

# Do this now
read -s -p "Password: " password

backup() {
  7z ... -p$password
}

# Do this starting at 5am - how?
while true; do
  backup
  sleep 86400
done

我看过at,但手册页非常少,没有任何示例。我不知道是否或如何安排功能。我需要将该函数提取到外部文件吗?

如果这是 XY 问题,我想做的是每天早上 5 点进行受密码保护的备份。脚本的第一部分通过 请求密码read,并通过环境变量将其提供给7z。归档部分可以放入一个while true;休眠 86400 秒的循环中,以实现“日常”部分。问题是从凌晨 5 点开始的。cron似乎不是一个好的选择,因为密码应该保存在文件中,这比输入一次密码更不安全。

答案1

如果我的数学是正确的:

sleep $(( 86400 + 5*3600 - $(date +%s) % 86400 ))
echo "it's 5 in the morning (UTC)"

$(date +%s)= 自 1970 年开始以来的当前时间(以秒为单位)(UTC);
$(date +%s) % 86400= 当天的部分,即自上个午夜 (UTC) 以来的秒数
86400 + 5*3600= 从上个午夜到明天凌晨 5 点的秒数
86400 + 5*3600 - $(date +%s) % 86400= 从现在到明天凌晨 5 点的秒数 (UTC)

当然,如果当前时间位于 UTC 午夜和目标时间之间,则这种情况会中断;并且您需要调整当地时区的目标时间......

也许更简单一点是:

sleep $(( $(date -d '05:00 tomorrow' +%s) - $(date +%s) ))

如果已经过了午夜,也会跳过额外的 24 小时,但至少它会计算当地时间。

因为密码应该保存在一个文件中,这比输入一次密码更不安全。

根据您想要防范的内容,您可能会将密码放入某些内存文件系统中tmpfs,这样它就不会到达磁盘。在 systemd 系统上,/run应该是tmpfs,但最好确保。

另外,根据您进行备份的方式,您可能会安排一个 SSH 密钥,该密钥只​​能用于将备份发送到远程(但不能读取它们)。或者使用公钥加密,这样您只需要在写入备份的主机上拥有备份的公共部分......

答案2

您不能at运行一个 shell 函数,但您可以at发送一个信号,在正在运行的脚本中等待该信号,并在信号到达时启动该函数。

如果此脚本运行为root那么您不需要--userfor systemd-run

#! /bin/bash

backup() {
  : 7z ... -p$password
}

trap sighandler_usr1 USR1

sighandler_usr1 () {
  trap '' USR1 # do not execute backup() several times in parallel
  echo "PID $$ just received SIGUSR1"
  backup
  trap sighandler_usr1 USR1
  request_signal
}

request_signal () {
  : use e.g. at or systemd-run to send SIGUSR1 to this shell
  systemd-run --user --on-calendar=05:00 kill -USR1 $$
}

request_signal

while true; do
  read -s -n 1000 ignore
done

相关内容