在 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
那么您不需要--user
for 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