Aller au contenu
BackFrontTips

Gérez les commandes introuvables dans le shell

Personnalisons la gestion des erreurs de commandes introuvables pour agrémenter l'expérience développeur.

Photo by Pierre Bamin / Unsplash

Si vous avez déjà travaillé dans un environnement de type Unix, vous avez sans doute remarqué cette erreur quand vous tentiez d'exécuter une commande inexistante.

$ cqt output.txt
zsh: command not found: cqt

Que se passe-t-il sous le capot ? Le manuel Linux nous l'explique.

... Si la recherche est infructueuse, l'interpréteur de commandes recherche une fonction définie de l'interpréteur de commandes nommée command_not_found_handle.  

Si cette fonction existe, elle est invoquée dans un environnement d'exécution distinct avec la commande d'origine et les arguments de la commande d'origine comme arguments, et l'état de sortie de la fonction devient l'état de sortie de ce sous-shell.

Si cette fonction n'est pas définie, l'interpréteur de commandes affiche un message d'erreur et renvoie un état de sortie de 127.

(Extrait traduit du manuel Linux)

Dans notre cas, nous n'avons pas (encore) défini la fonction command_not_found_handle, donc un message d'erreur est affiché et le code de sortie est 127.

$ cqt output.txt
zsh: command not found: cqt
$ echo $?
127

handle ou handler ?

Précision importante avant de commencer. Pour le shell Bash (l'interprète par défaut sur de nombreux systèmes Linux), la fonction à implémenter est command_not_found_handle. Pour le shell ZSH, le shell par défaut pour macOS depuis la version Catalina, il faudra implémenter le command_not_found_handler. Cependant, bash reste toujours disponible sur les MAC modernes.

Si les deux shells partagent beaucoup de similitudes, nous allons nous concentrer sur le shell Bash pour le reste de l'article, qui a l'« avantage » d'être présent sur les systèmes Linux et les macOS récents.

Les mains dans le code

Ouvrons le fichier de configuration de shell ~/.bashrc et ajoutons la fonction command_not_found_handle.

command_not_found_handle() {
    echo "Commande introuvable"
    echo "Veuillez vérifier votre saisie ou utiliser une autre commande."
}

Pour prendre en compte les modifications dans la session shell en cours, n'oubliez pas de lancer source ~/.bashrc.

$ vi ~/.bashrc
$ source ~/.bashrc
$ sfeir
Commande introuvable
Veuillez vérifier votre saisie ou utiliser une autre commande.

On peut enrichir la fonction en exploitant les arguments à notre disposition.

command_not_found_handle() {
    local command="$1"
    echo "Oh non ! La commande '$command' est introuvable."
    return 42
}

Comme le manuel l'indique, la fonction peut récupérer la commande d'origine. On peut donc l'utiliser comme variable. Ici, on décide de l'afficher. On peut aussi modifier le code de sortie. Dans le cas ci dessous, le code est non nul pour signaler que la commande introuvable est un échec.

command_not_found_handle() {
    local command="$1"
    echo "Commande introuvable : $command"
    commandes_similaires=$(apropos --long "$command" | awk '{print $1}')
    if [ -n "$commandes_similaires" ]; then
        echo "Vouliez-vous dire l'une de ces commandes similaires ?"
        echo "$commandes_similaires"
    fi
}

Dans l'exemple suivant, on va utiliser la variable comme paramètre d'autres fonctions. apropos retourne les pages du manuel en lien avec la commande puis awk transforme la sortie. Ainsi, quand on va saisir une commande inexistante, une liste d'alternatives sera proposée.

$ wge
Commande introuvable : wge
Vouliez-vous dire l'une de ces commandes similaires ?
wget

Ne plus réinventer la roue

Nous avons codé pour découvrir et comprendre la fonction command_not_found_handle mais il existe des outils qui se basent sur ce qu'on a vu et vont beaucoup plus loin.

Ubuntu et Debian possèdent un package command-not-found qui peut aller chercher dans les dépôts si on essaie de lancer un programme introuvable sur la machine. Pour les utilisateurs de Homebrew et macOS, le Homebrew Command Not Found permet de reproduire ce comportement. Enfin pour les fans de Oh My Zsh, la fonctionnalité similaire se trouve dans le plugin command-not-found.

Dernier