#!/bin/sh # public domain by Nathan Wagner # Assume N bytes total. # Assume L is the lowest number of files allowable. # H is the highest number of files allowable. # F is the actual number of files used # B is the minimum bytes per file # R is the remaining bytes if all files are of size B # K is the maximum number of files allowed to be one byte larger than the # minimum, K < F # # So, you need to determine if there is some L <= F <= H such that R <= K. # # For a given candidate F: # B = floor(N / F) # R = N % B # if R <= K then the candidate F is allowable, F files will be used, # R of them will be of size B+1 and F-R of them will be of size B. N= L= H= K= ECHO= VERBOSE=0 set -e while getopts l:h:k:n:ev flag ; do case $flag in l) L="$OPTARG" ;; h) H="$OPTARG" ;; k) K="$OPTARG" ;; n) N="$OPTARG" ;; e) ECHO=echo ;; v) VERBOSE=1 ;; ?) printf "Usage: evensplit [-lhknev] \n" 1>&2; exit 2 ;; esac done shift $(($OPTIND - 1)) INPUT=$1 N=$(stat -c '%s' $INPUT) : ${L:=$((N/65536))} # default to at least 65536 bytes per split if [ $L -lt 1 ]; then L=1; fi : ${H:=$N} # use one byte per file if needed if [ $H -lt $L ]; then H=$L; fi : ${K:=$((H-1))} # by default K can by any number of the files status=1 if [ $VERBOSE -ne 0 ]; then echo '#' F files: $L '<= F <=' $H, at most $K files one byte larger 1>&2 fi for F in $(seq $L $H); do B=$(($N / $F)) R=$(($N % $B)) if [ $R -le $K ]; then if [ $VERBOSE -ne 0 ]; then printf "# Usable: %u files, size %u" $((F-R)) $B 1>&2 if [ $R -gt 0 ]; then printf ", %u size %u", $R $((B+1)) 1>&2 fi printf "\n" 1>&2 fi status=0; break; fi done if [ $? -ne 0 ]; then echo "no usable splits found" 1>&2; exit $status fi DD="dd if=$INPUT count=1" # ok, at this point we want F-R files of size $B for ext in $(seq -w 1 $F); do x=$(echo $ext | sed -e 's/^0\+//') if [ $x -gt $((F-R)) ]; then ${ECHO} $DD of=${INPUT}.$ext bs=$((B+1)) skip=$(((F-R) * B)) iflag=skip_bytes else ${ECHO} $DD of=${INPUT}.$ext bs=$B skip=$((x-1)) fi done