前言
当哈希校验不通过时,适用本软件恢复。
本篇为数据管理第三步:
文件筛选整理 -> 生成哈希文件 -> 生成冗余数据 -> 定时同步多处备份
第二步请参考这篇文章:哈希校验及监测
冗余恢复软件简介
假如生成了5%的冗余数据,当任意部分数据丢失不超过5%时,可以恢复丢失的部分。称得上是保护个人数据的神器。冗余数据的大小与设置的块大小及冗余百分率有关。当设置了5%的恢复率及块大小约4096时(-s4096参数),冗余数据占用的空间约原文件的5%左右。
- 本方案适用范围
适用于需要长时间存储的数据。除了备份一份外,加上冗余数据是最省成本及最安全的做法,因为占用空间小。
冗余软件选择
Parchive 系列(par2格式) 或 winrar的“添加 winrar 恢复记录“ 。使用 Parchive 系列软件生成单独存在的恢复记录,恢复块与存档放在一起,可以校验及恢复记录。最方便灵活就是Parchive 系列(par2格式) 。都是用了 里德-所罗门码(Reed–Solomon error correction)实现冗余。该算法已经纳入了磁盘阵列 RAID 5/6 中。
小结
记录的数据要同时写下一份SHA256或blake3等哈希检验信息。方便知道那些数据已经失效。点击该检验信息自动校验。
有加密的数据要多留备份及加“恢复数据”,因为加密的数据稍有损坏,就会打不开,解密不回来。
添加冗余数据不一定可以恢复(损坏多于冗余率时),如果有两个备份,损坏位置不一样,可以用 par2 软件结合第 2 个文件(备份文件)来恢复。
定时同步多处备份可以使用FreeFileSync, rsync,Syncthing,BorgBackup,rclone
等软件同步,多种介质异地备份。放到云端的数据可用cryptomator
等软件自动透明加密。遵守备份的3-2-1 原则即可。不开篇细说。
Parchive 客户端总结
多平台命令行用: par2cmdline
只有命令行,全平台。在终端执行命令 par2
。其它平台界面基本上都是基于这个命令行核心的。多文件操作时适用。
Windows 平台
Windows 下直接用MultiPar 即可,这是最完善的 par2 客户端,图形界面与命令行都有,还自带命令行批处理脚本示例。BUG 修复快,细节满分,教程详细,多国语言。备用par2cmdline。—-建议去GitHub为这两个项目打star支持,最好有捐赠支持开发。
MultiPar 的安装目录(或解压目录)下,这个文件有对应的说明,很详细的教程,包括参数设置,0804 文件夹对应是中文的。包含生成 par2 的批处理示例,设置好批处理里面的 par2j.exe
的路径即可用:./Help/0804/index.Htm
/Help/ 目录下有命令行的参数说明。
注:不管用那个平台,都可以参考 MultiPar 的帮助文件入门 par2 。
Linux 平台
Linux 下主用:par2cmdline
Linux下 GUI 界面用: Easy Par2 ,PyPar2
Debian 及 ubuntu 可以用以下命令安装。其它 Linux 发行版可以搜关键字 par2cmdline
及 par2
来查看相关包名。
apt install par2
MacOS 平台
macOS 下主用:par2cmdline
macOS 下 GUI 界面用: MacPAR deLuxe
安装命令:
brew install par2
备用客户端
推荐使用上面的par2客户端,以下为备用客户端。可能随着时间情况会有变化。
一、 Par2deep
版本 V1.9.4 的路径处理有 BUG,例如带英文中括号的路径就不能识别。版本 V1.9.5 系列有其它 BUG,无法正常用。
Par2deep 全平台,有图形界面及命令行,用 Python 及 PyQT5 写的可以递归操作。安装后在终端执行命令 par2deep
或 nohup par2deep &
命令行可以用 par2deep-cli
。
可以用 pyinstaller 结合 pipreqs 确认依赖,然后打包成一个文件:
# 进入项目目录后
pipreqs ./ --encoding=utf-8 --force
# 会得到 requirements.Txt 文件,导入依赖到环境
python3 -m pip install -r requirements. txt
# 然后用 PyInstaller 打包成单独一个可以执行的文件。
python3 -m PyInstaller x1/x2/xxx.py --clean --onefile
其它 python打包类似可用 Nuitka
二、ParPar
只能创建 PAR2 文件,不能验证及修复。但高性能。
三、gopar
用 go 语言写的。
生成数据冗余文件
少量文件,直接用对应平台的图形界面客户端即可,不用怎么管理。
以下示例均是基于 par2cmdline 的命令行,Linux 与 macOS 通用。批量处理文件适用。
Windows 可以用 MultiPar 自带的批处理示例,本教程不重复造轮子。
方式一:单个或少量文件生成
用各平台对应的图形界面客户端软件即可,设置好冗余数据比例。
生成,验证,修复均可在界面直接点击。
方式二:本目录及子目录内所有文件批量生成
特点:生成的所有 par2 文件在执行的目录,不可以针对单独一个文件校验修复。验证或修复的时候会同时验证其余正常的文件,会较费不必要的时间。灵活性低。后期增减单个文件麻烦。适合不移动数据目录及不添加修改的文件夹,最适合什么都不改动的存档文件(例如非常适合光盘内容)。所有 par2 文件都在一个目录内,方便管理 par2 文件。
先 cd
进入该目录,运行
生成:
# -R 递归到子目录中生成。
par2 create -n1 -r5 -s2048 -R *
验证:
par2 verify *.par2
修复:
par2 repair *.par2
方式三:有选择性批量生成
灵活性高。可选排除适合在硬盘上生成的文件类型。Par2 文件存放在对应数据文件的目录下的隐藏文件夹下(示例的文件夹以 .hide_par2dir
命名)。
块大小:-s,数值越大,产生的冗余文件越小,下面表示2048字节块大小即2KB块大小。冗余率:-r,可损坏的百分比。冗余文件数量:-n,写 1 代表共生成 2 个 par2 文件,如此类推。
递归批量生成命令:添加 -n1 为只生成两个 par2 文件,-r5 为 5%的冗余数据率。生成的par2文件与源文件在同一个目录下。
find . -maxdepth 6 ! -path "*/.hide_par2dir/*" -type f ! -name "._*" -type f ! -name "sha256.sum" -type f ! -name "blake3.sum" -type f ! -name ".DS_Store" -type f -print | xargs -I {} par2 create -n1 -r5 -s2048 ./{}.par2 ./{}
# 验证
par2 verify abc.txt.par2 abc.txt
# 修复
par2 repair abc.txt.par2 abc.txt
移动生成的冗余文件
本节适用于上述的第三种方法产生的 par2 文件。不是用第三种方法的可跳过本节。
修改移动的脚本,加入生成的命令,当该目录所有文件都生成par2文件时,移动到隐藏文件夹。或者一步到位,直接生成par2文件在隐藏文件夹里。
批量移动 par2 校验文件到 par2 的隐藏目录。在每个含 par2 文件的目录下生成 . Hide_par2dir 隐藏文件夹,并移动 par2 文件到该目录。
只处理当前目录
把以下命令存入一个 .sh
文件,添加执行权限:
#!/bin/bash
files=$(ls ./*.par2 2> /dev/null | wc -l)
if [ "$files" -ne 0 ] ;then
if [ ! -d "./.hide_par2dir" ]; then mkdir .hide_par2dir ;fi;
mv ./*.par2 ./.hide_par2dir/ ;
fi
注意:上述命令只处理当前目录下的par2文件。
递归处理子目录
以下为上面命令的递归子目录版本,移动所有 par2
文件到该目录下的新建的隐藏目录.hide_par2dir
。
#!bin/bash
## 脚本功能: 批量移动par2校验文件到par2的隐藏目录.
## 在每个含par2文件的目录下生成 .hide_par2dir 隐藏文件夹, 并移动par2文件到该目录.
# dir=$(pwd) # 当前工作目录,不一定是脚本目录
# dir='/data/' # 某固定目录
dir="$(cd `dirname $0`; pwd)" # 脚本文件所在目录
# dir 为要处理的初始目录.
hidedir='.hide_par2dir' # 要隐藏的par2目录名
function move_file_to_par2dir(){
cd "$1"
temp_ls_data=$( ls "$1" );
oldIFS=$IFS # 更改分隔符为换行符,不然for循环会按空格分隔.
IFS=$'\n'
mvtime=0 ; # 初始移动par2文件次数
for result in ${temp_ls_data}
do
dir_or_file="$1"'/'${result}
if [ -d "${dir_or_file}" ];then # 是目录的话
# 当不是par2隐藏目录,递归遍历.
if [[ "${dir_or_file}" != "$1"'/'"${hidedir}" ]];
then move_file_to_par2dir "${dir_or_file}" ;
fi
else # 如果是文件的话
if [ ${mvtime} -eq 0 ] ;then
files_sum=$(echo "${temp_ls_data}" | grep '\.par2$' | wc -l)
# 如果当前目录含有par2文件
if [ "${files_sum}" -ne 0 ] ;then
if [ ! -d "./${hidedir}" ]; then mkdir "./${hidedir}" ;fi;
mv ./*.par2 ./${hidedir}/ ;
mvtime=1 ; # 当前目录下所有par2文件已经移动过
fi
fi
fi
done
IFS=$oldIFS # 还原初始分隔符
}
move_file_to_par2dir "${dir}"
隐藏冗余文件目录
如果不使用 Windows 可以跳过本节。
上述步骤以点开头的文件夹,已经实现了在 Linux 和 macOS 下隐藏文件夹的目的,以下为 windows 下隐藏该文件夹的方法。
移动生成的所有 *.par2
文件到一个固定格式名称的隐藏目录下,平时不放在一起,用到时候再复制(移动)出来修复。文件夹名称加点号,Windows 下 cmd 设置隐藏属性 attrib "文件名" +s +h
,参考教程 。(提高)有脚本检测删除了什么文件,以便清理 par2 文件。
Windows 系统隐藏文件夹批处理:
@echo off
title hide par2 folder
for /r /d %%i in ( .hide_par2dir ) do ( if exist "%%i" attrib +s +h "%%i" )
rem pause
Windows 系统显示文件夹批处理:
@echo off
title show par2 folder
for /r /d %%i in ( .hide_par2dir ) do ( if exist "%%i" attrib -s -h "%%i" )
rem pause
说明:上述批处理保存为 .cmd
后缀或.bat
文件。只有第 3 行 for 循环那行是必须的。如果要在窗口输入,把 %%i
换成 %i
。Rem 为注释,设置 @echo on
及取消注释 pause
可以显示实际处理信息。可以把.hide_par2dir
换成想要隐藏的文件夹名。Windows 下不能界面生成点开头的文件夹名,只能命令生成。
修复故障文件
实现:把哈希异常的文件自动运行 par2 命令修复。
步骤:
- 发生故障时记录“故障的目录“,记录到固定路径的文件中(例如 home 下文件名
err_par2dir.txt
)。脚本读取err_par2dir.txt
文件里的故障目录列表后第一个 for 循环,cd 进入到该故障目录,读取该目录的故障文件列表,第二个 for 循环。一般没有那么多文件故障,循环只是逻辑需要。 - 每一行就是一个文件,从文件提取故障的“文件名“。
- 运行
par2
相关命令修复,指定故障文件与修复文件 ,路径要拼接好,对应的目录拼接为转移后的目录。
其余命令参考par2 -h
的说明。
MacOS 系统,自动修复文件脚本:
#!bin/bash
## 脚本功能: 读取错误目录列表及相应损坏文件列表,自动运行par2修复错误命令。
## 如果成功修复,删除备份的损坏文件。删除错误目录及文件列表。
# dir=$(pwd) # 当前工作目录,不一定是脚本目录
# dir='/data/' # 某固定目录
# dir="$(cd `dirname $0`; pwd)" # 脚本所在目录
hidedir='.hide_par2dir' # 要隐藏的par2文件的目录名
def_err_filename='sum_fail.txt' # 默认在故障目录存放故障文件名的txt文件名
def_err_dirname='/var/tmp/err_par2dir.txt' # 存放在固定路径的, 故障对应目录的文件名. 不要写 ~ 等变量,不同身份运行会变的.
# 如果文件不存在就退出
if [ ! -f "${def_err_dirname}" ] ;then exit ;fi ;
# 去除重复行
## sort -u "${def_err_dirname}" | tee "${def_err_dirname}"
## awk '!a[$0]++' "${def_err_dirname}" | tee "${def_err_dirname}"
# 加载故障目录数据
## errdata_dir_names=$( cat "${def_err_dirname}" ) ;
errdata_dir_names=$( sort -u "${def_err_dirname}" | tee "${def_err_dirname}" )
if [ -n "${errdata_dir_names}" ] ;then # 如果有故障目录的数据
oldIFS=$IFS # 更改分隔符为换行符,不然for循环会按空格分隔.
IFS=$'\n'
# 循环目录
for temp_dirname in ${errdata_dir_names}
do
cd ${temp_dirname} ; # 进入该故障目录
errdata_filenames=$( cat "${temp_dirname}"'/'"${def_err_filename}" ) ;
# 循环目录下的文件名
for tempfilename in ${errdata_filenames}
do
# echo '-----内for循环开始------'
# 先移动par2文件(实际是改名)出来,再恢复错误的文件.恢复完后再移动回去
mv "$(pwd)"'/'"${hidedir}"'/'"${tempfilename#*./}"'.par2' "$(pwd)"'/'
mv "$(pwd)"'/'"${hidedir}"'/'"${tempfilename#*./}"'.vol'*'.par2' "$(pwd)"'/'
# 必须同一个目录才能修复. 原生自带的par2命令加 -p 参数,当修复成功时,删除备份的损坏文件同时删除par2备份文件. 是直接删除而不是移动到回收站.
repair_status=$( par2 repair "${temp_dirname}"'/'"${tempfilename#*./}"'.par2' "${temp_dirname}""${tempfilename:1}" | grep 'Repair complete.' | wc -l )
# 监听判断是否成功, 读取输出记录,看有没有成功关键字.成功就输出到特定文件记录
if [ $(eval echo ${repair_status}) -gt 0 ] && [ -f "${temp_dirname}"'/'"${tempfilename#*./}"'.1' ] ; then
# 删除备份的损坏文件
rm "${temp_dirname}"'/'"${tempfilename#*./}"'.1'
# 用 sed 删除文件列表里的该文件名所在的行
# macos 上要加上 -e 参数,sed -i '' -e ,Linux上 sed -i 就可 .
sed -i '' -e '/^\.\/'"${tempfilename:2}"'$/d' "${temp_dirname}"'/'"${def_err_filename}"
fi
# 移动par2备份文件回原来位置
mv "${temp_dirname}"'/'"${tempfilename#*./}"'.par2' "${temp_dirname}"'/'"${hidedir}"'/'
mv "${temp_dirname}"'/'"${tempfilename#*./}"'.vol'*'.par2' "${temp_dirname}"'/'"${hidedir}"'/'
# echo '-----内for循环结束------'
done
# 最里面的循环结束后删除该错误文件
# 当文件内容为空时候, 才删除.
if [ ! -s "${temp_dirname}"'/'"${def_err_filename}" ] ; then
rm "${temp_dirname}"'/'"${def_err_filename}" ;
#echo "---def_err_filename内容为空,删除---"
fi
# 删除单项故障目录, macos 上要加上 -e 参数,sed -i '' -e ,Linux上 sed -i 就可 .
temp_rm_linenumber=$(grep -wn "${temp_change_sedstr}" "${def_err_dirname}" | cut -d : -f 1)
sed -i '' -e "${temp_rm_linenumber}"'d' "${def_err_dirname}"
done
if [ ! -s "${def_err_dirname}" ] ; then
rm "${def_err_dirname}" ;
#echo "---def_err_dirname内容为空,删除---"
fi
IFS=$oldIFS # 还原初始分隔符
fi
Linux 系统,修复文件脚本,其余内容与macOS的脚本相同:
# 大约51及69行,把 sed -i '' -e 换成 sed -i 即可:
sed -i '/^\.\/'"${tempfilename:2}"'$/d' "${temp_dirname}"'/'"${def_err_filename}"
Windows系统,参考 MultiPar 程序自带的批处理示例及帮助教程。也可直接界面点击操作修复。