我过去曾使用几个 Bash 脚本将手工制作的文件存储库迁移到散列存储库,以便从 Web 应用程序(主要是 PHP 应用程序)访问和管理。在这些存储库中,文件名被散列(以避免与具有相同内容/名称的文件发生冲突)并且文件均匀分布(以确定性方式或随机方式)以保持每个目录的文件数较低,以提高性能。以下是一个完整的示例:
#!/bin/bash
MAXFILESPERDIR=500
TARGETROOTDIR="./newrepository"
RANDOMDISTRIBUTION=1
if [ -d "$1" ]; then
LOGFILE=$(basename $0).$(date +"_%Y%m%d_%H%M").${$}.log
SQLFILE=$(basename $0).$(date +"_%Y%m%d_%H%M").${$}.sql
SOURCEDIR="$1"
TOTALSOURCEFILES=$(find "$1" -type f | wc -l)
let "TOTALTARGETDIRS=$TOTALSOURCEFILES / $MAXFILESPERDIR"
PADLENTARGETDIRS=${#TOTALTARGETDIRS}
PADLENTARGETFILE=${#TOTALSOURCEFILES}
echo "We will create $TOTALTARGETDIRS directories to hold $MAXFILESPERDIR files per directory."
if [ "$RANDOMDISTRIBUTION" == "1" ] ; then
echo "We will rename and distribute each file randomly."
else
echo "We will rename and distribute each file uniformly."
fi
echo "Do you want to continue?"
select choice in yes no ; do
if [ "$choice" == "yes" ] ; then
COUNTER=1
find "$1" -type f | while read SOURCEFILE ; do {
CHECKSUMFILE=$(sha1sum "$SOURCEFILE" | cut -d " " -f 1)
CHECKSUMNAME=$(echo "$SOURCEFILE" | sha1sum | cut -d " " -f 1)
DETERMINISTICNONCE=$(printf "%0${PADLENTARGETFILE}d\n" $COUNTER)
if [ "$RANDOMDISTRIBUTION" == "1" ] ; then
PROBABILISTICNONCE=$(let "XX=$RANDOM % $TOTALTARGETDIRS + 1" ; printf "%0${PADLENTARGETDIRS}d\n" $XX;)
else
PROBABILISTICNONCE=$(let "XX=$COUNTER % $TOTALTARGETDIRS + 1" ; printf "%0${PADLENTARGETDIRS}d\n" $XX;)
fi
FILEDATE=$(stat -c %z "$SOURCEFILE" | cut -d "." -f 1)
FILESIZE=$(stat -c %s "$SOURCEFILE")
echo "Source file $SOURCEFILE" >> $LOGFILE
echo "Target file $TARGETROOTDIR/$PROBABILISTICNONCE/$PROBABILISTICNONCE$CHECKSUMFILE$DETERMINISTICNONCE" >> $LOGFILE
echo "INSERT INTO files (Filename, Location, Checksum, CDate, Size) VALUES ('$PROBABILISTICNONCE$CHECKSUMFILE$DETERMINISTICNONCE', '$PROBABILISTICNONCE', '$CHECKSUMFILE', '$FILEDATE', $FILESIZE);" >> $SQLFILE
mkdir -p $TARGETROOTDIR/$PROBABILISTICNONCE
cp -v "$SOURCEFILE" $TARGETROOTDIR/$PROBABILISTICNONCE/$PROBABILISTICNONCE$CHECKSUMFILE$DETERMINISTICNONCE
let "COUNTER+=1"
} ; done
echo "Done."
echo
break
fi
if [ "$choice" == "no" ] ; then
echo
echo "Operation cancelled"
echo
break
fi
done
else
echo
echo "Missing source directory"
echo
fi
只需从新存储库的根目录运行它。你可以通过修改第一个变量来配置它:MAXFILESPERDIR 定义每个目录存储多少个文件,TARGETROOTDIR 是创建一级目录的一级目录的名称(它只使用两级,第一级真的是单级的) root),而 RANDOMDISTRIBUTION 定义文件是随机分布(它可能看起来不均匀,特别是对于小运行)还是确定性分布(只是计数)。
它是如何工作的(仅供参考,以防这不是您想要的,但也许您可以获得一些想法):
- 计算源文件。
- 计算将创建多少个目标目录。
- 要求确认。
- 对于每个文件:
- 计算文件内容的 SHA1 哈希。
- 创建一个确定性的随机数。
- 创建一个概率随机数(如果 RANDOMDISTRIBUTION 为 1,否则只是一个计数器)。
- 获取尺寸和修改日期。
- 将随机值的值与散列和计数器组合以获得新的文件名(路径将是随机值)。
- 记录源和目标完整路径。
- 创建并记录 SQL 插入查询。
- 创建目标目录(如果不存在)。
- 复制文件。(如果你愿意,你可以移动它,但我很安全)。
- 结束
如果您将 RANDOMDISTRIBUTION 设置为 1 并多次运行脚本,您将获得源文件的副本,因为每次运行每个文件都会获得不同的目标文件名/路径。如果 RANDOMDISTRIBUTION 设置为其他值,则每次运行脚本时,文件都将以相同的方式重命名(对于相同的文件集,如果添加或删除文件,它们将获得不同的名称/路径)。
使用随机值 + 哈希 + 计数器的目的是确保我们可以处理重复文件(由于计数器不会发生冲突),同时仍然随机分布文件(对于足够长的运行,这将均匀分布文件)。
另外,生成的文件名的前缀也是目录名,所以如果你有文件名和目录名长度,你可以计算目录名(以防万一你没有将它存储在数据库中桌子)。
最后,这是一个一次性迁移脚本,它并没有真正编写为在同一组文件上定期执行。