#!/usr/bin/env bash # Audit des fichiers .desktop : chemins manquants, apps cachées, erreurs de syntaxe DIRS=( "$HOME/.local/share/applications" "/usr/share/applications" ) RED='\033[0;31m' YEL='\033[0;33m' GRN='\033[0;32m' BLU='\033[0;34m' RST='\033[0m' ok=0 warn=0 err=0 check_file() { local f="$1" local name name=$(basename "$f") local issues=() local hints=() # Ignorer les fichiers qui ne sont pas des Application affichables local type type=$(grep -m1 "^Type=" "$f" 2>/dev/null | cut -d= -f2-) [[ "$type" != "Application" ]] && return # Détecter si c'est un gestionnaire d'URL ou un fichier système (NoDisplay=true y est normal) local is_url_handler=false grep -q "^MimeType=.*x-scheme-handler" "$f" 2>/dev/null && is_url_handler=true local is_user_dir=false [[ "$f" == "$HOME/.local/share/applications/"* ]] && is_user_dir=true # NoDisplay ou Hidden : signaler uniquement dans le dossier utilisateur et si pas gestionnaire d'URL local nodisplay hidden nodisplay=$(grep -m1 "^NoDisplay=" "$f" | cut -d= -f2-) hidden=$(grep -m1 "^Hidden=" "$f" | cut -d= -f2-) if [[ "$is_user_dir" == true && "$is_url_handler" == false ]]; then [[ "$nodisplay" == "true" ]] && issues+=("caché (NoDisplay=true) — n'apparaît pas dans le menu") [[ "$hidden" == "true" ]] && issues+=("caché (Hidden=true) — n'apparaît pas dans le menu") fi # Exec : extraire le premier token (sans %F %u etc.) local exec_line exec_bin exec_line=$(grep -m1 "^Exec=" "$f" | cut -d= -f2-) # Retirer les guillemets englobants éventuels et les arguments %x exec_bin=$(echo "$exec_line" | awk '{print $1}' | tr -d '"' | sed 's/%[a-zA-Z]$//') if [[ -z "$exec_bin" ]]; then issues+=("Exec manquant") elif [[ "$exec_bin" == /* ]]; then # Chemin absolu if [[ ! -f "$exec_bin" ]]; then issues+=("exécutable introuvable : $exec_bin") elif [[ ! -x "$exec_bin" ]]; then issues+=("exécutable non exécutable : $exec_bin") fi else # Commande dans le PATH (ignorer les wrappers shell) if [[ "$exec_bin" != "sh" && "$exec_bin" != "bash" && "$exec_bin" != "env" ]]; then if ! command -v "$exec_bin" &>/dev/null; then issues+=("commande introuvable dans PATH : $exec_bin") fi fi fi # Icône (chemin absolu uniquement — les noms de thème sont difficiles à vérifier) local icon icon=$(grep -m1 "^Icon=" "$f" | cut -d= -f2-) if [[ "$icon" == /* && ! -f "$icon" ]]; then hints+=("icône introuvable : $icon") fi # Validation syntaxique (desktop-file-validate) if command -v desktop-file-validate &>/dev/null; then local syntax_out syntax_out=$(desktop-file-validate "$f" 2>&1 | grep -v "^$") while IFS= read -r line; do [[ -z "$line" ]] && continue if echo "$line" | grep -q "error:"; then issues+=("syntaxe : ${line#*error: }") elif echo "$line" | grep -q "warning:"; then hints+=("syntaxe : ${line#*warning: }") fi done <<< "$syntax_out" fi # Affichage local max_hints=3 if [[ ${#issues[@]} -gt 0 ]]; then echo -e "${RED}✗ $name${RST}" for i in "${issues[@]}"; do echo -e " ${RED}→ $i${RST}" done local shown=0 for h in "${hints[@]}"; do (( shown >= max_hints )) && { echo -e " ${YEL}~ … (${#hints[@]} avertissements au total)${RST}"; break; } echo -e " ${YEL}~ $h${RST}" (( shown++ )) done (( err++ )) elif [[ ${#hints[@]} -gt 0 ]]; then echo -e "${YEL}~ $name${RST}" local shown=0 for h in "${hints[@]}"; do (( shown >= max_hints )) && { echo -e " ${YEL}~ … (${#hints[@]} avertissements au total)${RST}"; break; } echo -e " ${YEL}~ $h${RST}" (( shown++ )) done (( warn++ )) else echo -e "${GRN}✓ $name${RST}" (( ok++ )) fi } for dir in "${DIRS[@]}"; do [[ -d "$dir" ]] || continue echo -e "\n${BLU}=== $dir ===${RST}" while IFS= read -r -d '' f; do check_file "$f" done < <(find "$dir" -maxdepth 1 -name "*.desktop" -print0 | sort -z) done echo "" echo -e "Résultat : ${GRN}$ok OK${RST} ${YEL}$warn avertissements${RST} ${RED}$err erreurs${RST}"