No debemos olvidar que la esencia de la tarea es bastante simple; como se puso en un tutorial sobre Haskell (que está escrito en torno a la solución de esta tarea, refinada gradualmente)
Ahora pensemos por un momento acerca de cómo funcionará nuestro programa y expresémoslo en pseudocódigo:
main = Read list of directories and their sizes.
Decide how to fit them on CD-Rs.
Print solution.
¿Suena razonable? Ya me lo imaginaba.
Simplifiquemos un poco nuestra vida y supongamos por ahora que calcularemos los tamaños de directorio en algún lugar fuera de nuestro programa (por ejemplo, con " du -sb *
") y leeremos esta información de stdin.
(de la guía Hitchhikers de Haskell, Capítulo 1 )
(Además, en su pregunta, le gustaría poder ajustar (editar) los diseños de disco resultantes y luego usar una herramienta para grabarlos).
Puede reutilizar (adaptar y reutilizar) una variante simple del programa de ese tutorial de Haskell para dividir su colección de archivos.
Desafortunadamente, en la distribute
herramienta que he mencionado aquí en otra respuesta , la simplicidad de la tarea de división esencial no se corresponde con la complejidad y la hinchazón de la interfaz de usuario distribute
(porque fue escrita para combinar varias tareas; aunque se realizó en etapas, pero aun así combinado no de la manera más limpia que se me ocurra ahora).
Para ayudarlo a utilizar su código, aquí hay un extracto del código bash de distribute
(en la línea 380 ) que sirve para realizar esta tarea "esencial" de dividir una colección de archivos:
# Splitting:
function splitMirrorDir() {
if [[ ! -d "$THIS_BASES_DIR/$BASE/$type" ]]; then
echo $"No base fixed for $type" >&2
exit 1
fi
# Getting the list of all suitable files:
local -a allFiles
let 'no = 0' ||:
allFiles=()
# no points to the next free position in allFiles
# allFiles contains the constructed list
for p in "$THIS_BASES_DIR/$BASE/$type"/*.rpm; do
if [[ ! -e "$p" ]]; then
# fail on non-existent files
echo $"Package file doesn't exist: " "$p" >&2
return 1
fi
if [[ "$ONLY_REAL_FILES" == "yes" && ! -f "$p" ]]; then
continue
fi
if [[ "$DIFF_TO_BASE" ]]; then
older_copy="$DIFF_TO_BASE/$type/${p##*/}" # using shell param expansion instead of `basename' to speed up
if [[ -h "$older_copy" || -a "$older_copy" ]]; then
continue
fi
fi
allFiles[$(( no++ ))]="$p"
done
readonly -a allFiles
# Splitting the list of all files into future disks:
#
local -a filesToEat allSizes
let 'no = 0' ||:
filesToEat=()
allSizes=($(getSize "${allFiles[@]}"))
readonly -a allSizes
# allSizes contains the sizes corrsponding to allFiles
# filesToEat hold the constructed list of files to put on the current disk
# no points to the next free position in filesToEat
# totalSize should hold the sum of the sizes
# of the files already put into filesToEat;
# it is set and reset externally.
for p in "${allFiles[@]}"; do
if (( totalsize + ${allSizes[$(( no ))]} > CDVOLUME )); then
eatFiles "${filesToEat[@]}"
filesToEat=()
finishCD
startTypedCD
fi
let "totalsize += ${allSizes[$(( no ))]}" ||:
filesToEat[$(( no++ ))]="$p"
done
eatFiles "${filesToEat[@]}"
}
function eatFiles() {
#{ oldIFS="$IFS"; IFS=$'\n'; echo "$FUNCNAME: args: " "$*" | head >&2; IFS="$oldIFS"; }
zeroDelimited "$@" | xargs -0 --no-run-if-empty \
cp -s \
--target-dir="$THIS_LAYOUTS_DIR/cd$(( cdN ))/$PREFIX/$type$DOT_SUFFIX"/ \
--
}
function startTypedCD() {
# set -x
mkdir -p "$THIS_LAYOUTS_DIR/cd$(( cdN ))/$PREFIX/$type$DOT_SUFFIX"
start_action $" %s with %s" "$(( cdN ))" "$type"
# set +x
}
function finishCD() {
( lea más después de la línea 454 )
Tenga en cuenta que la eatFiles
función prepara los diseños de los futuros discos como árboles donde las hojas son enlaces simbólicos a los archivos reales. Por lo tanto, cumple con su requisito de poder editar los diseños antes de grabar. La mkisofs
utilidad tiene una opción para seguir enlaces simbólicos, que de hecho se emplea en el código de mi mkiso
función .
El guión presentado (¡que puede tomar y reescribir según sus necesidades, por supuesto!) Sigue la idea más simple: para sumar los tamaños de los archivos (o, más precisamente, los paquetes en el caso de distribute
) solo en el orden en que se enumeraron, don No haga ningún reordenamiento.
La "Guía de autoestopistas de Haskell" toma el problema de optimización más en serio y sugiere variantes de programa que intentarían reorganizar los archivos de manera inteligente, para que se ajusten mejor a los discos (y requieran menos discos):
Ya hay suficientes preliminares. vamos a empacar algunos CD.
Como ya habrás reconocido, nuestro problema es clásico. Se llama un "problema de mochila"
( búscalo en Google , si aún no sabes qué es. Hay más de 100000 enlaces).
empecemos por la solución codiciosa ...
(lea más en el Capítulo 3 y más).
Otras herramientas inteligentes
También me han dicho que Debian usa una herramienta para hacer sus CD de distribución que es más inteligente que mis distribute
colecciones de paquetes wrt: sus resultados son mejores porque se preocupa por las dependencias entre paquetes y trataría de hacer la colección de paquetes que sigue el primer disco cerrado bajo dependencias, es decir, ningún paquete del primer disco debería requerir un paquete de otro disco (o al menos, diría, el número de tales dependencias debería minimizarse).