我有我已经在过去使用到m几个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是创建第一级目录的第一级目录的名称(它只使用两个级别,第一个是真正的单一目录根),RANDOMDISTRIBUTION定义文件是否随机分布(它可能看起来不均匀,特别是对于小批量运行)或确定性(只是计数)。
它是如何工作(仅供参考,以防万一这是不是你在找什么,但也许你可以得到一些想法):
- 计数的源文件。
- 计算将创建多少个目标目录。
- 请求确认。
- 对于每个文件:
- 计算文件内容的SHA1哈希值。
- 创建确定性随机数。
- 创建概率随机数(如果RANDOMDISTRIBUTION为1,否则只是一个计数器)。
- 获取大小和修改日期。
- 将随机值的值与散列和计数器组合以获得新文件名(路径将是随机值)。
- 记录源和目标完整路径。
- 创建并记录SQL插入查询。
- 创建目标目录(如果它不存在)。
- 复制文件。 (你可以移动它,如果你想,但我玩的很安全)。
- 完成
如果设置RANDOMDISTRIBUTION为1并运行该脚本几次,你会得到你的源文件的副本,因为每个文件将每次运行得到不同的目标文件名/路径它。如果将RANDOMDISTRIBUTION设置为其他内容,则每次运行脚本时,文件都将以相同的方式重命名(对于相同的文件集,如果添加或删除文件,它们将得到不同的名称/路径)。
使用随机值+散列+计数器的目的是确保我们可以处理重复(不会因计数器而发生碰撞),同时仍然随机分发文件(对于足够长的运行,这将分配文件均匀)。
此外,生成文件名的前缀也是该目录的名称,以便如果您有文件名和目录名称长度,则可以计算目录名称(以防万一您不存储在你的数据库表中)。
最后,这是一次性迁移脚本,它并没有真正写入通过同一组文件定期执行。