====== Outil de recherche dans l'historique d'apt ====== \\ Mise à jour le : 08/06/2023\\ Non fonctionnel avant cette date ! * Outil de recherche dans l'historique d'apt. * Recherche par motif de nom de paquet * Recherche par la date * Recherche par la commande d'appel d'//apt// * Recherche par le type d'action d'//apt// * Recherche possible dans les fichiers de logs archivés * Affichage humainement lisible * Script directement fonctionnel (pas de modification nécessaire). Nécessite //gawk// et //zcat// : apt install gawk gzip ===== L'aide ===== Apt History Research - ahr - 2023-06-08-22:05 Usage : ahr [-a | -z N] [--apt [-0]] [-c COMMAND] [-d DATE] [-t TYPE] MOTIF ahr -c COMMAND [-a | -z N] [--apt [-0]] [-d DATE] [-t TYPE] [MOTIF] ahr -d DATE [-a | -z N] [--apt [-0]] [-c COMMAND] [-t TYPE] [MOTIF] ahr -t TYPE [-a | -z N] [--apt [-0]] [-c COMMAND] [-d DATE] [MOTIF] Outil de recherche dans l'historique d'apt. Recheche sur MOTIF ou COMMAND ou DATE ou TYPE. Si plusieurs champs de recherche sont indiqués, alors applique un ET. Options : -a, --all Rechercher dans tout l'historique, même archivé. Imcompatible avec '-z'. -c, --command COMMAND Rechercher sur la commande COMMAND. -d, --date DATE Rechercher sur la date DATE affinée avec l'heure. Format : AAAA[-MM[-JJ[ HH[:MM[:SS]]]]] -h, --help Afficher cette aide et quitter. -t, --type TYPE Rechercher selon le TYPE, TYPE prend les valeurs : Install, Reinstall, Upgrade, Remove et Purge -z, --dezip N Rechercher dans le fichier archive N : /var/log/apt/history.log.N.gz Imcompatible avec '-a'. -v, --version Afficher la version et quitter. --apt N'afficher que la liste des noms des paquets. Le retour est utilisable par apt ou dpkg. Un nom de paquet par ligne. -0 Seulement avec '--apt'. Utiliser le caractère NULL pour séparateur de nom de paquet et pas le retour à la ligne. MOTIF est une partie ou un nom exact de paquet. COMMAND est une partie ou la commande appelant l'action d'apt. DATE est une partie ou la date de début de l'action d'apt. TYPE est le type d'action d'apt. Tawal®© ===== Le script ===== (mis à jour le 08/06/2023 22:05) non fonctionnel avant cette date ! #!/bin/bash # Nom : ahr Apt History Research # # Par : Tawal # # Nécessite : gawk (apt install gawk) # version=2023-06-08-22:05 ### Répertoire des logs d'apt (personnalisable) apt_folder="/var/log/apt" ### Formats Textes Surb='\e[1m' # Surbrillance Ital='\e[3m' # Italique Rst='\e[m' # Reset ### Expressions régulières # Entier (option -z) rgx_int="^[[:digit:]]+$" # Format de DATE (option -d.) rgx_date="^[[:digit:]]{4}($|-(0[1-9]|1[0-2])($|-(0[1-9]|[1-2][0-9]|3[0-1])($| ([0-1][0-9]|2[0-3])($|:[0-5][0-9]))))$" ### Fonctions usage_quit() { echo -e "Apt History Research - ahr - $version Usage : ${Surb}${0##*/} [-a | -z N] [--apt [-0]] [-c COMMAND] [-d DATE] [-t TYPE] MOTIF${Rst} ${Surb}${0##*/} -c COMMAND [-a | -z N] [--apt [-0]] [-d DATE] [-t TYPE] [MOTIF]${Rst} ${Surb}${0##*/} -d DATE [-a | -z N] [--apt [-0]] [-c COMMAND] [-t TYPE] [MOTIF]${Rst} ${Surb}${0##*/} -t TYPE [-a | -z N] [--apt [-0]] [-c COMMAND] [-d DATE] [MOTIF]${Rst} Outil de recherche dans l'historique d'apt. Recheche sur ${Surb}MOTIF${Rst} ou ${Surb}COMMAND${Rst} ou ${Surb}DATE${Rst} ou ${Surb}TYPE${Rst}. Si plusieurs champs de recherche sont indiqués, alors applique un ET. Options : -a, --all Rechercher dans tout l'historique, même archivé. Imcompatible avec '${Ital}-z${Rst}'. -c, --command ${Surb}COMMAND${Rst} Rechercher sur la commande ${Surb}COMMAND${Rst}. -d, --date ${Surb}DATE${Rst} Rechercher sur la date ${Surb}DATE${Rst} affinée avec l'heure. Format : ${Surb}AAAA[-MM[-JJ[ HH[:MM[:SS]]]]]${Rst} -h, --help Afficher cette aide et quitter. -t, --type ${Surb}TYPE${Rst} Rechercher selon le ${Surb}TYPE${Rst}, ${Surb}TYPE${Rst} prend les valeurs : ${Surb}Install${Rst}, ${Surb}Reinstall${Rst}, ${Surb}Upgrade${Rst}, ${Surb}Remove${Rst} et ${Surb}Purge${Rst} -z, --dezip ${Surb}N${Rst} Rechercher dans le fichier archive ${Surb}N${Rst} : ${Ital}$apt_folder/history.log.${Surb}N${Rst}${Ital}.gz${Rst} Imcompatible avec '${Ital}-a${Rst}'. -v, --version Afficher la version et quitter. --apt N'afficher que la liste des noms des paquets. Le retour est utilisable par ${Ital}apt${Rst} ou ${Ital}dpkg${Rst}. Un nom de paquet par ligne. -0 Seulement avec '${Ital}--apt${Rst}'. Utiliser le caractère NULL pour séparateur de nom de paquet et pas le retour à la ligne. ${Surb}MOTIF${Rst} est une partie ou un nom exact de paquet. ${Surb}COMMAND${Rst} est une partie ou la commande appelant l'action d'apt. ${Surb}DATE${Rst} est une partie ou la date de début de l'action d'apt. ${Surb}TYPE${Rst} est le type d'action d'apt." printf "%80s\n" "Tawal®©" exit 0 } version_quit() { echo "$version" exit } erreur_quit() { echo -n "${0##*/} : " [ "$1" = "log" ] && echo -e "Le fichier '$apt_folder/history.log' n'existe pas." [ "$1" = "zip" ] && echo -e "Le fichier '$apt_folder/history.log.$2.gz' n'existe pas." [ "$1" = "opt" ] && echo -e "Option ${Surb}'$2'${Rst} non-reconnue." [ "$1" = "opt0" ] && echo -e "Option '${Surb}-0${Rst}' utilisée sans '${Surb}--apt${Rst}'." [ "$1" = "motif" ] && echo -e "Motif absent ou pas d'utilisation d'une des options '${Surb}-c${Rst}' ou '${Surb}-d${Rst}' ou '${Surb}-t${Rst}'." [ "$1" = "opt_az" ] && echo -e "Options '${Surb}-a${Rst}' et '${Surb}-z${Rst}' incompatibles." [ "$1" = "opt_z" ] && echo -e "L'argument de l'option ${Surb}'-z'${Rst} doit être un nombre." [ "$1" = "opt_t" ] && echo -e "L'option ${Surb}-t${Rst} ne prend que les mots ${Surb}Install${Rst}, ${Surb}Reinstall${Rst}, ${Surb}Upgrade${Rst}, ${Surb}Remove${Rst} et ${Surb}Purge${Rst}." [ "$1" = "opt_d" ] && echo -e "Date incorrecte : ${*:2}" [[ $1 =~ gawk|zcat ]] && echo -e "Nécessite ${Ital}${BASH_REMATCH[0]}${Rst} : apt install ${BASH_REMATCH[0]}" exit 1 } >&2 test_opt() { [ "$1" = "opt_t" ] && [ "$2" != "Install" ] && \ [ "$2" != "Remove" ] && [ "$2" != "Upgrade" ] && \ [ "$2" != "Reinstall" ] && [ "$2" != "Purge" ] && return 1 [ "$1" = "opt_d" ] && ! [[ "$2" =~ $rgx_date ]] && return 1 [ "$1" = "opt_z" ] && ! [[ "$2" =~ $rgx_int ]] && return 1 return 0 } input2treat() { local i f n zap="$1" declare -a LogFich idx shift if [ "$1" = "all" ] then for f in "$apt_folder"/hist*gz do n="${f%.*}"; n="${n##*.}" LogFich[n]="$f" done # shellcheck disable=SC2206 idx=( ${!LogFich[@]} ) for ((i=${#idx[@]}-1; i>=0; i--)) do zcat "${LogFich[${idx[i]}]}" done [ ! "$zap" ] && cat "$apt_folder"/history.log elif [ "$1" = "one" ] then zcat "$apt_folder"/history.log."$2".gz else cat "$apt_folder"/history.log fi } ### Gestion des Options. shopt -s extglob nullglob while getopts :ac:d:ht:vz:-:0 option do case $option in a) opt_a="on" [ "$opt_z" ] && erreur_quit opt_az dezip_type="all" ;; c) opt_c="$OPTARG" ;; d) opt_d="$OPTARG" test_opt opt_d "$opt_d" || erreur_quit opt_d "$opt_d" ;; z) opt_z="on" [ "$opt_a" ] && erreur opt_az test_opt opt_z "$OPTARG" || erreur_quit opt_z dezip_type="one" dezip_nb="$OPTARG" ;; h) usage_quit ;; t) opt_t="$OPTARG" test_opt opt_t "$opt_t" || erreur_quit opt_t ;; v) version_quit ;; 0) opt_null="on" ;; -) case $OPTARG in all) opt_a="on" [ "$opt_z" ] && erreur_quit opt_az dezip_type="all" ;; apt) opt_apt="on" ;; command) opt_c="${!OPTIND}" OPTIND=$((OPTIND+1)) ;; date) opt_d="${!OPTIND}" test_opt opt_d "$opt_d" || erreur_quit opt_d OPTIND=$((OPTIND+1)) ;; dezip) opt_z="on" [ "$opt_a" ] && erreur opt_az test_opt opt_z ${!OPTIND} || erreur_quit opt_z dezip_type="one" dezip_nb=${!OPTIND} OPTIND=$((OPTIND+1)) ;; help) usage_quit ;; type) opt_t=${!OPTIND} test_opt opt_t "$opt_t" || erreur_quit opt_t OPTIND=$((OPTIND+1)) ;; version) version_quit ;; *) erreur_quit opt --"$OPTARG" ;; esac ;; *) erreur_quit opt -"$OPTARG" ;; esac done shift $((OPTIND-1)) if ! hash gawk 2>/dev/null then erreur_quit gawk fi if ! hash zcat 2>dev/null then erreur_quit zcat fi if [ "$1" ] then rech="$1" elif [ ! "$opt_c" ] && [ ! "$opt_d" ] && [ ! "$opt_t" ] then erreur_quit motif fi if [ "$opt_null" ] && [ ! "$opt_apt" ] then erreur_quit opt0 fi if [ "$opt_z" ] then [ -e "$apt_folder"/history.log."$dezip_nb".gz ] || erreur zip "$dezip_nb" else if ! [ -e "$apt_folder"/history.log ] then if [ "$opt_a" ] then zap="on" else erreur_quit log fi fi fi ### Corps du programme # shellcheck disable=SC1004 gawk -vrech="$rech" -vopt_c="$opt_c" -vopt_d="$opt_d" -vopt_t="$opt_t" -vapt="$opt_apt" -vnull="$opt_null" ' function afficher(verb, zero, mess, Array, ArrayTmp, tmp, k,n) { if (0 in Array) { if (!verb && mess) {print mess} for (n in Array) { if (tmp && Array[n] ~ /,/) { ArrayTmp[k++]=Array[n] continue } if (!verb) {printf("\t")} printf(Array[n]) if (zero) {printf("\0")}else{printf("\n")} } } } BEGIN { PROCINFO["sorted_in"]="@val_str_asc" } { if ($1 ~/Start/) { start="Date Début : "$2" "$3 date=$2" "$3 ok=0 next } if ($1 ~ /Commandline/) { command=gensub($1" ", "", 1) next } if ($1 ~ /Requested-By/) { demand=gensub($1" ", "", 1) next } if ($1~ /End/) { end="Date Fin : "$2" "$3 ok=1 } if (opt_c && command ~ opt_c) {opt_c_ok=1} if (opt_d && date ~ opt_d) {opt_d_ok=1} if (opt_t && $1 ~ opt_t) {opt_t_ok=1} if (rech && $0 ~ rech) {rech_ok=1} if ((opt_c_ok && opt_d_ok && rech_ok && opt_t_ok) || \ (!opt_c && opt_d_ok && rech_ok && opt_t_ok) || \ (!opt_c && !opt_d && rech_ok && opt_t_ok) || \ (opt_c_ok && !opt_d && rech_ok && opt_t_ok) || \ (opt_c_ok&& opt_d_ok && !rech && opt_t_ok) || \ (!opt_c && opt_d_ok && !rech && opt_t_ok) || \ (opt_c_ok && !opt_d && !rech && opt_t_ok) || \ (opt_c_ok && opt_d_ok && rech_ok && !opt_t) || \ (!opt_c && opt_d_ok && rech_ok && !opt_t) || \ (!opt_c && !opt_d && rech_ok && !opt_t) || \ (opt_c_ok && !opt_d && rech_ok && !opt_t) || \ (opt_c_ok && opt_d_ok && !rech && !opt_t) || \ (!opt_c && opt_d_ok && !rech && !opt_t) || \ (opt_c_ok && !opt_d && !rech && !opt_t) || \ (!opt_c && !opt_d && !rech && opt_t_ok)) { ok1=1 } opt_c_ok=0; opt_d_ok=0; opt_t_ok=0; rech_ok=0 if (ok1) { for (v=2; v<=NF-1; v++) { if ($v !~ /,/) { if (apt) { paq=$v }else{ if ($(v+2) ~ /)/) { der=gensub(",", "", 1, $(v+2)) paq=$v" "$(v+1)" "der }else{ pre=gensub(",", "", 1, $(v+1)) paq=$v" "pre } } if ($1 ~ /Install:/) { I[a++]=paq }else if ($1 ~ /Upgrade:/) { U[b++]=paq }else if ($1 ~ /Remove:/) { R[c++]=paq }else if ($1 ~ /Purge:/) { P[d++]=paq }else if ($1 ~ /Reinstall:/) { Re[e++]=paq } } } } if (ok && ok1) { if (!apt) { printf("\n") print start print end if (command) {print "Commande : "command} if (demand) {print "Demandeur : "demand} } afficher(apt, null, "Installés :", I, Itmp, "on") afficher(apt, null, "", Itmp) afficher(apt, null, "Upgradés :", U) afficher(apt, null, "Désinstallés :", R) afficher(apt, null, "Purgés :", P) afficher(apt, null, "Réinstallés :", Re) if (!apt) { if (a>1) {s1="s"}else{s1=""} if (b>1) {s2="s"}else{s2=""} if (c>1) {s3="s"}else{s3=""} if (d>1) {s4="s"}else{s4=""} if (e>1) {s5="s"}else{s5=""} if (a>0) {print a,"paquet"s1" installé"s1} if (b>0) {print b,"paquet"s2" upgradé"s2} if (c>0) {print c,"paquet"s3" désinstallé"s3} if (d>0) {print d,"paquet"s4" purgé"s4} if (e>0) {print e,"paquet"s5" réinstallé"s5} printf("\n") } delete I; delete U; delete R; delete P; delete Re; delete Itmp command="" date="" demand="" a=0; b=0; c=0; d=0; e=0; ok=0; ok1=0 } } ' < <(input2treat "$zap" "$dezip_type" "$dezip_nb") ===== Exemples d'utilisation et de retours ===== === Afficher tout l'historique (archives comprises) === ahr -a . === Afficher l'historique non archivé concernant un paquet === ahr youtube-dl Date Début : 2023-03-02 09:55:13 Date Fin : 2023-03-02 09:55:57 Commande : apt -t bullseye-backports install youtube-dl Demandeur : tawal (1000) Upgradés : youtube-dl:amd64 (2021.06.06-1, 2021.12.17-1~bpo11+1) 1 paquet upgradé === Afficher l'historique du mois de mars 2023 === ahr -ad 2023-03 Date Début : 2023-03-01 22:28:53 Date Fin : 2023-03-01 22:29:14 Commande : apt purge linux-image-5.10.0-19-amd64 Demandeur : tawal (1000) Purgés : linux-image-5.10.0-19-amd64:amd64 () 1 paquet purgé Date Début : 2023-03-02 09:47:51 Date Fin : 2023-03-02 09:49:38 Commande : apt install yt-dlp Demandeur : tawal (1000) Installés : yt-dlp:amd64 (2023.02.17-1~bpo11+1) python3-brotli:amd64 (1.0.9-2+b2, automatic) python3-mutagen:amd64 (1.45.1-2, automatic) python3-pycryptodome:amd64 (3.9.7+dfsg1-1+b2, automatic) python3-websockets:amd64 (8.1-1, automatic) 5 paquets installés Date Début : 2023-03-02 09:55:13 Date Fin : 2023-03-02 09:55:57 Commande : apt -t bullseye-backports install youtube-dl Demandeur : tawal (1000) Upgradés : youtube-dl:amd64 (2021.06.06-1, 2021.12.17-1~bpo11+1) 1 paquet upgradé === Afficher l'historique des désinstallations d'unattended-upgrade dans le fichier archive 1 === ahr -c unattended -t Remove -z1 Date Début : 2023-04-29 23:34:02 Date Fin : 2023-04-29 23:34:23 Commande : /usr/bin/unattended-upgrade Demandeur : tawal (1000) Désinstallés : linux-headers-5.10.0-20-amd64:amd64 (5.10.158-2) 1 paquet désinstallé Date Début : 2023-04-29 23:34:38 Date Fin : 2023-04-29 23:34:48 Commande : /usr/bin/unattended-upgrade Désinstallés : linux-headers-5.10.0-20-common:amd64 (5.10.158-2) 1 paquet désinstallé Date Début : 2023-04-29 23:35:05 Date Fin : 2023-04-29 23:35:41 Commande : /usr/bin/unattended-upgrade Désinstallés : linux-image-5.10.0-20-amd64:amd64 (5.10.158-2) 1 paquet désinstallé