Nous ne devons pas oublier que l'essence de la tâche est en effet assez simple; comme mis dans un tutoriel sur Haskell (qui est écrit autour de l'élaboration de la solution pour cette tâche, progressivement améliorée)
Réfléchissons maintenant un instant au fonctionnement de notre programme et exprimons-le en pseudocode:
main = Read list of directories and their sizes.
Decide how to fit them on CD-Rs.
Print solution.
Semble raisonnable? J'ai pensé ainsi.
Simplifions un peu notre vie et supposons pour l'instant que nous allons calculer la taille des répertoires quelque part en dehors de notre programme (par exemple, avec " du -sb *
") et lire ces informations depuis stdin.
(du guide des auto-stoppeurs à Haskell, chapitre 1 )
(De plus, dans votre question, vous aimeriez pouvoir modifier (modifier) les dispositions de disque résultantes, puis utiliser un outil pour les graver.)
Vous pouvez réutiliser (adapter et réutiliser) une variante simple du programme de ce tutoriel Haskell pour fractionner votre collection de fichiers.
Malheureusement, dans l' distribute
outil que j'ai mentionné ici dans une autre réponse , la simplicité de la tâche de fractionnement essentielle n'est pas compensée par la complexité et le gonflement de l'interface utilisateur de distribute
(car il a été écrit pour combiner plusieurs tâches; bien qu'il soit effectué par étapes, mais toujours combiné pas de la manière la plus propre à laquelle je pouvais penser maintenant).
Pour vous aider à utiliser son code, voici un extrait du code bash de distribute
(à la ligne 380 ) qui sert à effectuer cette tâche "essentielle" de fractionnement d'une collection de fichiers:
# 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() {
( lire la suite après la ligne 454 )
Notez que la eatFiles
fonction prépare les dispositions des futurs disques sous forme d'arbres où les feuilles sont des liens symboliques vers les vrais fichiers. Ainsi, il répond à votre exigence de pouvoir modifier les dispositions avant de les graver. L' mkisofs
utilitaire a une option pour suivre les liens symboliques, qui est en effet utilisé dans le code de ma mkiso
fonction .
Le script présenté (que vous pouvez prendre et réécrire selon vos besoins, bien sûr!) Suit l'idée la plus simple: pour additionner les tailles des fichiers (ou, plus précisément, des packages dans le cas de distribute
) juste dans l'ordre dans lequel ils ont été listés, don ne fais pas de réarrangements.
Le "Guide des Hitchhikers pour Haskell" prend le problème d'optimisation plus au sérieux et suggère des variantes de programme qui essaieraient de réorganiser les fichiers intelligemment, afin qu'ils s'adaptent mieux aux disques (et nécessitent moins de disques):
Assez de préliminaires déjà. allons emballer quelques CD.
Comme vous l'avez peut-être déjà reconnu, notre problème est classique. C'est ce qu'on appelle un "problème de sac à dos"
( google it up , si vous ne savez pas déjà ce que c'est. Il y a plus de 100000 liens).
commençons par la solution gourmande ...
(lire plus dans le chapitre 3 et plus loin.)
Autres outils intelligents
On m'a également dit que Debian utilise un outil pour rendre ses CD de distribution plus intelligents que mes distribute
collections de paquets wrt: ses résultats sont plus agréables car il se soucie des dépendances inter-paquets et essaierait de rendre la collection de paquets qui fonctionne le premier disque fermé sous des dépendances, c'est-à-dire qu'aucun paquet du 1er disque ne devrait exiger un paquet d'un autre disque (ou du moins, je dirais, le nombre de ces dépendances devrait être minimisé).