我刚刚学会了如何编写简单的 shell 脚本。其中之一就是简单地复制系统中对我来说至关重要的某些文件。
cp /dir/dir/file1 /samedir/samedir/file1.bak
每次登录系统时,我的脚本都会执行。但是它总是会覆盖file1.bak
。我想找到一种方法来保留所有创建的文件。也许是类似的东西file1(1).bak
,file1(2).bak
或者类似的东西。
VMS 在某些方面非常棒,即每次有人写入一个名为的文件时file.bak
,它都会通过在文件末尾添加尾随的“:”来表示版本号,从而保留所有以前的版本,即:,,,file.bak:1
等等......file.bak:2
file.bak:521
我们过去常常使用清除命令来删除以前的版本
purge file.bak 1-520
。
答案1
你可以使用一些供应链管理软件(也称为版本控制),git
例如mercurial
或fossil
。
这样,您可以避免在工作目录中存储大量bak
文件,同时跟踪所有以前的版本。
在我看来最简单的一个是化石-SCM 它是静态编译的单个文件,可以处理此类任务。(顺便说一下,它是由创建 SQLite 的同一位程序员创建的,因此质量很高)
你的工作流程可能是这样的:
cd
到需要跟踪文件的目录并使用命令初始化存储库fossil init
(它将准备一个数据库文件)- 告诉 Fossil 跟踪特定目录:
fossil open
- 递归地将文件/目录添加到存储库
fossil addremove --dotfiles
- 将目录的当前状态保存到存储库:
fossil commit
或者如果您关心文件的完整性,那么:fossil commit --sha1sum
所有后续调用fossil
都只是fossil commit
(如果您需要添加更多新文件或从跟踪中删除一些文件,那么只需在提交之前发出fossil addremove --dotfiles
)
这样,对文件的所有更改都将保存在时间线中,并且可以从存储库中逐个文件进行预览、比较(差异)或提取(签出),或者完全恢复整个目录的先前状态。
您可以使用fossil extras --dotfiles
它来显示新的未跟踪文件或仅发现已更改的文件fossil changes --abs-paths --sha1sum --header -v
如果您需要排除某些文件或目录,Fossil 也支持它。
您可以通过同一个文件生成的漂亮的 Web 界面查看更改的时间线,甚至可以使用内部 WiKi 对更改进行漂亮的注释。
如果您需要从存储库中删除某些内容,fossil
可以提供一种称为“回避”的机制来执行此操作。
答案2
解决方案
如果您不想使用实际的版本控制系统(如@Alex&@Class Stacker 所建议的),那么以下 bash 脚本应该可以完成这项工作:
#!/bin/bash
#############
# Variables #
#############
FILE_LIST="foo.txt bar.doc" # Space separated list of files to backup (include full path to file)
KEEP_OLD="5" # How many previous versions to keep
#############
# Functions #
#############
function shift_backups {
for num in $(seq $KEEP_OLD -1 1) ; do
old_backup="$file.bak$num"
if [[ -e $old_backup && $num == $KEEP_OLD ]] ; then
echo " removing oldest file ($old_backup)"
rm -f $old_backup
elif [[ -e $old_backup ]] ; then
new_name="$file.bak$(expr $num + 1)"
echo " moving $old_backup to $new_name"
mv $old_backup $new_name
fi
done
}
################
# Backup Files #
################
for file in $FILE_LIST ; do
count=1
while [[ $count -le $KEEP_OLD ]] ; do
backup_file="$file.bak$count"
if [[ -e $backup_file ]] ; then
echo "$backup_file exists, shifting backups"
shift_backups
cp $file $backup_file
break
else
cp $file $backup_file
break
fi
count=$(expr $count + 1)
done
done
解释
为了解释这一点,我将把它分解并向您展示其中的逻辑。
变量
- 该
FILE_LIST
变量包含要包含在备份中的所有文件的空格分隔列表(确保使用完整的文件路径) - 该
KEEP_OLD
变量指示何时应删除 .bak 文件。在示例中,它设置为 5,这意味着您的备份不会回溯超过 5 个版本(foo.txt.bak5 是最旧的版本)
注意:根据您要执行此操作的文件数量,可能需要调整 FILE_LIST 变量以引用文件并循环遍历其中的每一行。[我还没有测试过这个想法]
功能
该函数shift_backups
执行以下操作:
从最旧到最新循环查看备份文件
for num in $(seq $KEEP_OLD -1 1) ; do
如果备份文件存在且是最早的计数,则删除它
if [[ -e $old_backup && $num == $KEEP_OLD ]]
或者如果备份文件仅存在,它将被转移到
backup number + 1
elif [[ -e $old_backup ]]
备份循环
遍历变量中的每个文件
FILE_LIST
for file in $FILE_LIST ; do
当变量值为
count
1时继续循环执行备份检查while [[ $count -le $KEEP_OLD ]] ; do
将目标文件名存储在变量中
backup_file="$file.bak$count"
如果备份文件存在,则调用 shift_backups 函数,然后复制文件(发生这种情况时会中断 while 循环)
echo "$backup_file exists, shifting backups" shift_backups cp $file $backup_file break
如果备份文件不存在,则仅复制并退出 while 循环
else cp $file $backup_file break
增加变量
count
count=$(expr $count + 1)
输出
该脚本中包含一些回显,以帮助理解它所采取的步骤。例如,下面是在最新版本的备份文件为 .bak3 的目录中运行时脚本的输出:
foo.txt.bak1 exists, shifting backups
moving foo.txt.bak3 to foo.txt.bak4
moving foo.txt.bak2 to foo.txt.bak3
moving foo.txt.bak1 to foo.txt.bak2
bar.doc.bak1 exists, shifting backups
moving bar.doc.bak3 to bar.doc.bak4
moving bar.doc.bak2 to bar.doc.bak3
moving bar.doc.bak1 to bar.doc.bak2
如果您需要脚本安静运行,请删除回声,它就会以相同的方式运行。