正在运行:Ubuntu 18.04 (gnome)(如果重要的话,还安装了 xfce4 包)
cron版本:3.0pl1-128.1ubuntu1
上:amd64
更新:所有软件包都是最新的,并且据我所知没有问题
内核:5.0.0-29-通用#31~18.04.1-Ubuntu
我正在尝试运行一个备份脚本,我知道这个脚本是有效的,因为我已经测试过了。我还知道,如果我去掉 while 循环,cronjob 将会运行,脚本将会成功执行。我希望保留 while 循环,因为我希望备份在 dropbox 更新后进行。我已将 echo 命令放在 while 循环中,然后观察日志文件上 cat 的输出 -n1,发现每 10 秒记录到文件中的内容是“Dropbox 未运行!”但我知道这是因为如果我运行 dropbox status,它会显示“已更新”。以下是脚本:
#!/bin/bash
if [[ $(($(date +%A-%B-%d-%Y) != $(cat /home/user/Log/backup.log | tail -n1 | cut -d " " -f7))) \
-eq 1 ]]
then
while [[ $(dropbox status) != "Up to date" ]]
do
echo $(dropbox status) >> /home/user/Log/backup.log
sleep 10s
done
shopt -s dotglob globstar
if [[ $(($(date +%d) % 2)) -eq 0 ]]
then
rm -rf /backup/daily0/home/* >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily0/etc/* >> /home/user/Log/backup.log 2>&1
cp -r --preserve /home/user/*/backup/daily0/home >> /home/user/Log/backup.log 2>&1
cp -r --preserve /etc/* /backup/daily0/etc >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily0/home/Dropbox/.dropbox >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily0/home/Dropbox/.dropbox.cache >> /home/user/Log/backup.log 2>&1
echo "successful backup of /home/user and /etc: $(date +%A-%B-%d-%Y) $(date +%l:%M%P)" >>\
/home/user/Log/backup.log
else
rm -rf /backup/daily1/home/* >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily1/etc/* >> /home/user/Log/backup.log 2>&1
cp -r --preserve /home/user/* /backup/daily1/home >> /home/user/Log/backup.log 2>&1
cp -r --preserve /etc/* /backup/daily1/etc >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily1/home/Dropbox/.dropbox >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily1/home/Dropbox/.dropbox.cache >> /home/user/Log/backup.log 2>&1
echo "successful backup of /home/user and /etc: $(date +%A-%B-%d-%Y) $(date +%l:%M%P)" >>\
/home/user/Log/backup.log
fi
exit 0
else
exit 0
fi
这是 sudo crontab:
@reboot /usr/local/bin/dailyBackup
我尝试过几件事:
@reboot sleep 60 && /usr/local/bin/dailyBackup
@reboot /usr/bin/dropbox start && /usr/local/bin/dailyBackup
@reboot /usr/bin/dropbox start && sleep 60 && /usr/local/bin/dailyBackup
我也尝试过在脚本本身中放置 dropbox start 命令,并将 sleep 60 作为脚本中的第一个命令,但无论我做什么,由 cron 运行的脚本中的 dropbox status 输出都是“Dropbox 未运行!”。因此,cron 在启动或重启时运行脚本,但一旦 dropbox 确实启动,“dropbox status”命令的输出不会更新。我该怎么做才能解决这个问题?
编辑:
目的是明确说明,在系统正常运行期间启动后调用时,脚本可以在 while 循环中完美运行,例如:
sudo dailyBackup
还尝试在脚本中指定 dropbox 命令的完整路径,例如:
/usr/bin/dropbox status
这也不起作用。
答案1
我已经找到如何解决这个问题的方法。我读到过以下内容:https://www.linux.com/news/introduction-services-runlevels-and-rcd-scripts/然后我将脚本复制到 /etc/init.d 并从 /etc/rc5.d 为其创建符号链接,如下所示:
sudo ln -s ../init.d/dailyBackup ./S01dailyBackup
现在,当我启动后看到 /home/user/Log/backup.log 中记录的内容是:
Dropbox isn't running!
Checking for changes...
successful backup of /home/user and /etc: Sunday-September-29-2019 at 10:06am
无论如何,这感觉比在启动时运行 cronjob 更可靠。以下是脚本的最终版本:
#!/bin/bash
if [[ $(($(date +%A-%B-%d-%Y) != $(cat /home/user/Log/backup.log | tail -n1 | cut -d " " -f7))) \
-eq 1 ]]
then
while [[ $(sudo -u user dropbox status) != "Up to date" ]]
do
echo $(sudo -u user dropbox status) >> /home/user/Log/backup.log
sleep 10s
done
shopt -s dotglob globstar
if [[ $(($(date +%d) % 2)) -eq 0 ]]
then
if [[ -e /backup/daily0/user ]]
then
rm -rf /backup/daily0/user/ >> /home/user/Log/backup.log 2>&1
fi
if [[ -e /backup/daily0/etc ]]
then
rm -rf /backup/daily0/etc/ >> /home/user/Log/backup.log 2>&1
fi
cp -r --preserve /home/user/ /backup/daily0/ >> /home/user/Log/backup.log 2>&1
cp -r --preserve /etc/ /backup/daily0/ >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily0/user/Dropbox/.dropbox >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily0/user/Dropbox/.dropbox.cache >> /home/user/Log/backup.log 2>&1
echo "successful backup of /home/user and /etc: $(date +%A-%B-%d-%Y) $(date +%l:%M%P)" >>\
/home/user/Log/backup.log
else
if [[ -e /backup/daily1/user ]]
then
rm -rf /backup/daily1/user/ >> /home/user/Log/backup.log 2>&1
fi
if [[ -e /backup/daily1/etc ]]
then
rm -rf /backup/daily1/etc/ >> /home/user/Log/backup.log 2>&1
fi
cp -r --preserve /home/user/ /backup/daily1/ >> /home/user/Log/backup.log 2>&1
cp -r --preserve /etc/ /backup/daily1/ >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily1/user/Dropbox/.dropbox >> /home/user/Log/backup.log 2>&1
rm -rf /backup/daily1/user/Dropbox/.dropbox.cache >> /home/user/Log/backup.log 2>&1
echo "successful backup of /home/user and /etc: $(date +%A-%B-%d-%Y) $(date +%l:%M%P)" >>\
/home/user/Log/backup.log
fi
exit 0
else
exit 0
fi
我尝试使用 7Z 将目录压缩成一个存档,这样每次备份确实节省了大约 10GB,而且由于我随时保存 2 天的内容,因此节省了 20GB 的磁盘空间。问题是该脚本需要一个小时才能运行,因此我尝试使用 tar -cjf,这样每次备份可以节省大约 8GB,但脚本完成任务仍然需要近一个小时。因此,为了提高效率,我牺牲了 20GB 的磁盘空间,毕竟如今 Big-O 通常更为重要,除非处理嵌入式系统上非常小的容量。我很高兴能解决这个问题,我希望它能帮助那些尝试做类似事情的人:) 对我来说,最好备份所有这些文件和目录的最后 2 天,以防我想在对学校项目或 /etc 中的配置文件或其他地方进行更改之前检索旧版本。也许随着我学到更多,我将来会在备份中添加一些其他目录。在我的学校,我们所有计算机科学专业的学生都在基于 Debian 的学校服务器上拥有一个帐户,这真的很酷。我们都可以访问目录 /backup/daily.0/userName 到 /backup/daily.20/userName 中我们主目录的 21 天备份,这是我在保存 21 天前的 .bash_history 并将其与一天前的历史记录合并后产生的灵感。我编写了一个脚本,每 5 分钟通过 cronjob 将我的 .bash_history 和 .tmux_history 合并一次。在调试脚本时,我曾多次破坏这两个文件的内容,很高兴能够保存我的历史记录,因为我保留了一个无限长度的历史记录文件,我有一个脚本可以通过每年在 cronjob 上删除重复条目来截断它。
答案2
我选择不编辑前一个答案,因为当我单击添加这个答案时提示我这样做,这样对该答案的评论就不会无效,而且因为我觉得之前的答案和评论在它们反映的学习经验中很有价值。
我很兴奋,甚至在关闭机器之前就发布了之前的答案,发现仍然有某些运行中的问题。我还没有弄清楚问题到底是什么,我相信这是因为我的脚本不符合 LSB 规范并且位于 /etc/init.d 中。
我实际上现在已经解决了这个问题,并且这个解决方案在几天内一直很有效,在此期间我已经多次测试过它。
我做了什么:我在尝试让这个脚本在我的 Arch 安装中启动时运行的时候发现了这个帖子,该脚本位于此桌面的一个单独驱动器上:https://unix.stackexchange.com/questions/138281/arch-linux-run-script-a-minute-after-boot然后我再次浏览了这个很久没看过的页面:https://wiki.archlinux.org/index.php/Systemd以下解决方案在我的 Arch 机器上运行良好,并且意识到 Ubuntu 也运行 Systemd,因此我决定在 Ubuntu 上也尝试一下...成功:)
这是现在可行的解决方案:
#!/bin/bash
if [[ $(($(date +%A-%B-%d-%Y) != $(cat /home/user/Log/backup.log | tail -n1 | cut -d " " -f7))) \
-eq 1 ]]
then
while [[ $(sudo -u user dropbox status) != "Up to date" ]]
do
sleep 10s
[[ $(uptime | cut -d " " -f4) -ge 30 ]] && echo "Dropbox took to long on $(date)" >> \
/home/user/Log/backup.log && exit 0
done
shopt -s dotglob globstar
# Change to BACKUPNUM=$(($(date +%d) % 10)) when get separate drive to
# backup to some day
BACKNUM=$(($(date +%d) % 2))
rsync -aAX --delete /home/user/ /backup/daily${BACKNUM}/user/ >> \
/home/user/Log/backup.log 2>&1
[[ $? -eq 0 ]] && rsync -aAX --delete /etc/ /backup/daily${BACKNUM}/etc/ >> \
/home/user/Log/backup.log 2>&1
[[ $? -eq 0 ]] && rm -rf /backup/daily${BACKNUM}/user/.dropbox >> \
/home/user/Log/backup.log 2>&1
[[ $? -eq 0 ]] && rm -rf /backup/daily${BACKNUM}/user/Dropbox/.dropbox >> \
/home/user/Log/backup.log 2>&1
[[ $? -eq 0 ]] && rm -rf /backup/daily${BACKNUM}/user/Dropbox/.dropbox.cache >> \
/home/user/Log/backup.log 2>&1
[[ $? -eq 0 ]] && \
echo "successful backup of /home/user and /etc: $(date +%A-%B-%d-%Y) $(date +%l:%M%P)" >> \
/home/user/Log/backup.log
exit 0
else
exit 0
fi
所以我添加了以下两个文件 /etc/systemd/system/dailyBackup.service:
[Unit]
Description=dailyBackup
[Service]
Type=simple
ExecStart=/usr/local/bin/dailyBackup
和 /etc/systemd/system/dailyBackup.timer:
[Unit]
Desciption=Runs dailyBackup one minute after boot
[Timer]
#how long to wait before executing
OnBootSec=1min
Unit=dailyBackup.service
[Install]
WantedBy=multi-user.target
然后我跑了:
sudo systemctl enable dailyBackup.timer
并关闭系统。重新启动后,脚本成功执行,没有关机或重启问题。如前所述,这个解决方案已经工作了几天。从脚本中可以看出,我现在正在使用 rsync,这是 bacOn 在我的上一个回答的评论中建议的。我过去曾尝试过几次 rsync,但从未真正完全弄清楚为什么运行 diff -r 时仍然存在差异,但我想我已经意识到这些差异是由于系统 .files 在运行 rsync 后发生了轻微变化,也因为我的桌面上的 ~/snap/gnome-system-monitor/current/.local/share/icons/hicolor 中似乎有一些损坏的符号链接,而我的笔记本电脑 Ubuntu 上甚至没有 snap 目录。这一定是由于我之前安装和删除了某些东西。但这是另一个问题,因为 --delete rsync 标志似乎取消了 --exclude 标志,即使我添加了 --delete-excluded 标志也是如此。这就是为什么我的脚本在 rsync 之后仍然有 rm 语句。我希望这对某些人有帮助,我从中学到了很多东西,对我来说这是无价的。现在我需要做一些实际的学校作业,而不是玩和学习 Linux,这是我最喜欢做的事情之一:)
更新:
因此我发现 rsync 标志 --exclude={} 中的模式必须是相对路径,相对于正在同步的源目录,这就是完全忽略排除的原因。我输入的是绝对路径。所以不再需要 rm -rf 语句。我现在还在 while 循环中添加了一些条件,并将睡眠时间减少到一秒(我知道这接近轮询命令),这样如果因为我添加了一些巨大的视频文件或其他东西而导致 dropbox 花费太长时间更新,如果我在脚本仍在运行该 while 循环时关闭或重新启动,脚本将退出 0。因此,在我找到一种更好的方法在关机或重启时停止脚本,以及一种更好的方法让脚本等到 dropbox 更新之前,也许 dailyBackup.timer 中的一些东西甚至在 dropbox 更新之前都不运行它,这里是当前最佳工作版本的脚本:
#!/bin/bash
if [[ $((10#$(date +%d) != 10#$(cat /home/user/Log/backup.log | tail -n1 | cut -d " " -f7 | \
cut -d "-" -f3))) -eq 1 ]]
then
while [[ $(sudo -u user dropbox status) != "Up to date" ]]
do
[[ $(runlevel | cut -d " " -f2) -eq 0 ]] && exit 0
[[ $(runlevel | cut -d " " -f2) -eq 6 ]] && exit 0
[[ $(uptime | cut -d " " -f4) -ge 30 ]] && echo "Dropbox took to long on $(date)" >> \
/home/user/Log/backup.err && exit 0
sleep 1s
done
shopt -s dotglob globstar
# Change to BACKUPNUM=$(($(date +%d) % 10)) when get separate drive to backup to someday
BACKNUM=$((10#$(date +%d) % 2))
rsync -aAX --delete-excluded \
--exclude={".dropbox","Dropbox/.dropbox",".dropbox-dist","Dropbox/.dropbox.cache"} \
/home/user/ /backup/daily${BACKNUM}/user/ >> /home/user/Log/backup.err 2>&1
[[ $? -eq 0 ]] && rsync -aAX --delete /etc/ /backup/daily${BACKNUM}/etc/ >> \
/home/user/Log/backup.err 2>&1
[[ $? -eq 0 ]] && \
echo "successful backup of /home/user and /etc: $(date +%A-%B-%d-%Y) $(date +%l:%M%P)" >> \
/home/user/Log/backup.log
exit 0
else
exit 0
fi
我没有说明,但是为了使用它,我创建了一个 /backup 目录,然后创建了一个 /backup/daily0 和 /backup/daily1 以及“sudo chown me:myGroup”,然后在 daily0 和 daily1 内创建了一个用户和一个 etc 目录。
另外,在我的笔记本电脑上,我将 dailyBackup.timer 更改为在启动 3 分钟后启动脚本,因为 wifi 的启动时间比有线连接的启动时间更长,而在我的桌面上,我将其更改为 2 分钟,只是为了帮助它在 Dropbox 更新时间较长的情况下不会循环太久。
此外,当日期达到 08 时,我发现由于第一个条件 if 中的数字被解释为八进制,因此我必须在前面加上 10#,以便它在任何地方都使用十进制数字,这促使我只需添加一个额外的 cut -d 即可仅使用月份的实际日期,而不是比较整个日期字符串。本网站的问题是,代码块中的磅号被解释为注释的开头,因此看起来这些行中的最后一行只是注释,但事实并非如此。我还发现最好将错误发送到一个文件,将成功日志发送到另一个文件。
chebang 之后的第一行代码和实例化变量 BACKNUM 的行就是我所说的行。