#!/bin/bash REQUIREMENTS="wget" display_help() { cat << EOF Usage: $0 [OPTIONS] OPTIONS: -v, --verbose Active le mode verbeux. -d, --distro NAME Spécifie la distribution (defaut: bookworm). -a, --arch ARCH Spécifie l'architecture (defaut: amd64). -p, --packages LIST Liste des paquets à analyser (séparés par des virgules et sans espace, exemple : vim,openssh-server,netcat-traditional) EOF exit 0 } if [[ "$#" == 0 ]]; then display_help return 1 fi while [[ "$#" -gt 0 ]]; do case "$1" in -v|--verbose) VERBOSE=1 echo "Mode Verbeux activé." shift ;; -d|--distro) if [ -n "$2" ]; then DISTRO="$2" echo "Distrib des packages : $DISTRO" shift 2 # Déplace de 2 positions ($1 = -o, $2 = valeur) else echo "Erreur: --distro requiert un argument." >&2 exit 1 fi ;; -a|--arch) if [ -n "$2" ]; then ARCH="$2" echo "Architecture de la distrib : $ARCH" shift 2 # Déplace de 2 positions ($1 = -o, $2 = valeur) else echo "Erreur: --arch requiert un argument." >&2 exit 1 fi ;; -p|--packages) if [ -n "$2" ]; then PACKAGE_LIST="$2" echo "Paquets à analyser : $PACKAGE_LIST" shift 2 # Déplace de 2 positions ($1 = -o, $2 = valeur) else echo "Erreur: --packages requiert un argument." >&2 exit 1 fi ;; -h|--help) display_help shift ;; # Marqueur de fin d'options --) shift break # Arrête le traitement des options ;; # Autres arguments *) echo "Argument non reconnu: $1" >&2 display_help exit 1 ;; esac done # Distrib et Architecture par defaut if [ ! -n "$DISTRO" ];then DISTRO=bookworm;fi if [ ! -n "$ARCH" ];then ARCH=amd64;fi # Variables utilisateur MAX_PARALLEL=5 # On prévoit un nombre de thread curl/wget pour le téléchargement PACKAGES_FILE="Packages_${DISTRO}_${ARCH}.gz" # Autant variabiliser le nom de la liste des paquets PACKAGES_FILE_MAX_AGE=86400 # Age maximum autorisé pour la copie locale du fichier Packages # Liste des paquets non souhaités ou déjà installés EXCLUDED_PACKAGES=" debconf-2.0 adduser apparmor apt apt-utils base-files base-passwd bash bsdutils busybox console-setup console-setup-linux coreutils cpio cron cron-daemon-common dash debconf debconf-i18n debian-archive-keyring debianutils diffutils dmeventd dmidecode dmsetup dpkg e2fsprogs eject fdisk findutils firmware-linux-free gcc-12-base gettext-base gpgv grep grub-common grub-pc grub-pc-bin grub2-common gzip hostname ifupdown init init-system-helpers initramfs-tools initramfs-tools-core intel-microcode iproute2 iputils-ping isc-dhcp-client isc-dhcp-common iucode-tool kbd keyboard-configuration klibc-utils kmod less libacl1 libaio1 libapparmor1 libapt-pkg6.0 libargon2-1 libattr1 libaudit-common libaudit1 libblkid1 libbpf1 libbrotli1 libbsd0 libbz2-1.0 libc-bin libc-l10n libc6 libcap-ng0 libcap2 libcap2-bin libcom-err2 libcrypt1 libcryptsetup12 libdb5.3 libdebconfclient0 libdevmapper-event1.02.1 libdevmapper1.02.1 libedit2 libefiboot1 libefivar1 libelf1 libext2fs2 libfdisk1 libffi8 libfreetype6 libfuse2 libgcc-s1 libgcrypt20 libgmp10 libgnutls30 libgpg-error0 libgssapi-krb5-2 libhogweed6 libidn2-0 libip4tc2 libjansson4 libjson-c5 libk5crypto3 libkeyutils1 libklibc libkmod2 libkrb5-3 libkrb5support0 liblocale-gettext-perl liblvm2cmd2.03 liblz4-1 liblzma5 libmd0 libmnl0 libmount1 libncursesw6 libnettle8 libnewt0.52 libnftables1 libnftnl11 libp11-kit0 libpam-modules libpam-modules-bin libpam-runtime libpam0g libpci3 libpcre2-8-0 libpng16-16 libpopt0 libproc2-0 libreadline8 libseccomp2 libselinux1 libsemanage-common libsemanage2 libsepol2 libslang2 libsmartcols1 libss2 libssl3 libstdc++6 libsystemd-shared libsystemd0 libtasn1-6 libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl libtinfo6 libtirpc-common libtirpc3 libudev1 libunistring2 libusb-1.0-0 libuuid1 libxtables12 libxxhash0 libzstd1 linux-base locales login logrotate logsave lvm2 mawk mount nano ncurses-base ncurses-bin netbase nftables os-prober passwd pci.ids pciutils perl-base procps readline-common sed sensible-utils systemd systemd-sysv sysvinit-utils tar tasksel tasksel-data tzdata ucf udev usbutils usr-is-merged util-linux util-linux-extra vim-common vim-tiny whiptail xkb-data zlib1g zstd " # Nécessaire pour télécharger la liste des paquets et les paquets eux mêmes (pourront être écrasées si on voulait porter # le script vers une autre distribution) BASE_URL="https://deb.debian.org/debian/" PACKAGES_FILE_URL="dists/${DISTRO}/main/binary-${ARCH}/Packages.gz" # Initialiser les queues declare -A packages_to_download declare -a packages_queue get_package_info() { local current_package="$1" # Si le lien de téléchargement n'a pas encore été déterminé if [ -z "${packages_to_download["$current_package"]}" ]; then # Extraire les informations du paquet _PACKAGE_BLOCK=$(zcat $PACKAGES_FILE | sed -n "/^Package: $current_package$/,/^$/{p;/^$/q}") # Contrôler que l'on a bien des informations concernant le paquet if [ -n "$_PACKAGE_BLOCK" ]; then # Extraire le lien de téléchargement et l'affecter à la liste des paquets à télécharger _PACKAGE_RELATIVE_LINK=$(echo "$_PACKAGE_BLOCK" | grep -E "^Filename:" | cut -d ' ' -f 2) packages_to_download["$current_package"]="$_PACKAGE_RELATIVE_LINK" # Récupérer le noms des dépendances sous forme de string dont les noms des paquets sont séparés par des espaces _DEPENDENCY_LIST=$(echo "$_PACKAGE_BLOCK" | grep -E "^Depends:" | sed "s/([^)]*)/()/g" | sed "s/()//g" | sed 's/,//g' | sed 's/|//g' | tr -s ' '| cut -d ':' -f 2) # Ajouter les dépendances à la liste d'attente if [ -n "$_DEPENDENCY_LIST" ]; then for _DEPENDENCY in $_DEPENDENCY_LIST; do # Si la dépendance est présente dans la liste noire, on passe au suivant if [[ $EXCLUDED_PACKAGES == *" $_DEPENDENCY "* ]]; then continue fi # Si la dépendance est absente du tableau des paquets à télécharger, on l'y ajoute (clé vide ou non vide) if [ -z ${packages_to_download["$_DEPENDENCY"]+x} ]; then packages_to_download["$_DEPENDENCY"]="" # Ajout avec valeur vide packages_queue+=("$_DEPENDENCY") # Ajout dans la file d'attente fi done fi fi fi } populate_packages_to_download() { echo "Recherche des dépendances..." # Tant qu'il y a des paquets dans la file d'attente while [ ${#packages_queue[@]} -gt 0 ]; do # Récupérer le premier élément de la queue PACKAGE_TO_PROCESS="${packages_queue[0]}" # Traiter l'élément get_package_info "$PACKAGE_TO_PROCESS" # Enlever l'élément de la queue packages_queue=("${packages_queue[@]:1}") done } get_packagelist_file() { # Récupérer le fichier Packages.gz si la version locale est trop ancienne local download_needed="0" # Vérifier si le fichier est présent et s'il est suffisamment récent if [ -f $PACKAGES_FILE ]; then _PACKAGE_FILE_AGE=$(($(date +%s) - $(stat -c %Z "$PACKAGES_FILE"))) if [ $_PACKAGE_FILE_AGE -gt $PACKAGES_FILE_MAX_AGE ]; then download_needed=1 fi else download_needed=1 fi # Télécharger le fichier if [ $download_needed -eq 1 ]; then echo "Téléchargement de $PACKAGES_FILE" $_WGET $_WGET_PARAM "$BASE_URL$PACKAGES_FILE_URL" -O $PACKAGES_FILE else if [[ $VERBOSE == 1 ]]; then echo "$PACKAGES_FILE est suffisamment récent" fi fi } download_package() { if [[ $VERBOSE == 1 ]]; then echo "Démarrage du téléchargement parallèle (max $MAX_PARALLEL) en arrière-plan..." fi local PIDS=() # Itérer sur les clés (les noms de paquets) for PACKAGE_NAME in "${!packages_to_download[@]}"; do # Récupérer le lien relatif du paquet local PACKAGE_RELATIVE_LINK="${packages_to_download[$PACKAGE_NAME]}" # Si le lien est vide (ce qui ne devrait pas arriver si populate_packages_to_download a fonctionné) if [ -z "$PACKAGE_RELATIVE_LINK" ]; then echo "Avertissement : Lien de téléchargement introuvable pour $PACKAGE_NAME. Saut." >&2 continue fi # Lancer le téléchargement en arrière-plan if [[ $VERBOSE == 1 ]]; then echo "Lancement du téléchargement pour $PACKAGE_NAME..." fi $_WGET $_WGET_PARAM -P "deb_files" "$BASE_URL$PACKAGE_RELATIVE_LINK" & # Enregistrer le PID (Process ID) PIDS+=($!) # Supprimer le paquet du tableau associatif pour le marquer comme "en cours/traité" unset packages_to_download["$PACKAGE_NAME"] # Si on atteint le maximum de processus parallèles, attendre qu'ils se terminent if [[ ${#PIDS[@]} -ge $MAX_PARALLEL ]]; then if [[ $VERBOSE == 1 ]]; then echo "--- $MAX_PARALLEL téléchargements lancés. Attente de la fin des processus... ---" fi wait "${PIDS[@]}" PIDS=() # Vider le tableau de PID pour la prochaine batch fi done # Attendre la fin des derniers processus lancés (la dernière batch) if [[ ${#PIDS[@]} -gt 0 ]]; then if [[ $VERBOSE == 1 ]]; then echo "Attente de la fin des derniers processus ($?) en cours..." fi wait "${PIDS[@]}" fi echo "Téléchargement terminé." } debug_array() { echo "--- Liste finale des paquets uniques requis ---" for _KEY in ${!packages_to_download[@]}; do echo "$_KEY ----- ${packages_to_download[$_KEY]}" done } main() { if (which wget > /dev/null); then _WGET=$(which wget) else echo "Wget est requis" return 1 fi if [ -z "$PACKAGE_LIST" ]; then echo "Aucun paquet spécifié. Utilisez l'option -p ou --packages." >&2 return 1 # Sortir de la fonction main avec un code d'erreur fi if [[ $VERBOSE == 1 ]]; then _WGET_PARAM="" else _WGET_PARAM="-q" fi get_packagelist_file if [ -n "$PACKAGE_LIST" ]; then OLD_IFS=$IFS IFS=',' for PACKAGE in $PACKAGE_LIST; do PACKAGE=$(echo "$PACKAGE" | xargs) packages_to_download["$PACKAGE"]="" packages_queue+=("$PACKAGE") done IFS=$OLD_IFS populate_packages_to_download fi download_package } main