minishell est mon premier gros projet de groupe du cursus 42 : réécrire un shell interactif (un “mini-bash”) en C, avec un comportement réaliste sur tout ce qui est essentiel au quotidien : parsing, expansions, redirections, pipelines, builtins et signaux.

Le projet force à comprendre ce que le shell fait réellement : transformer une ligne de commande en une exécution correcte de processus, avec les bons file descriptors, les bons exit codes, et une gestion robuste des erreurs.

Objectifs

  • Construire un pipeline complet : lecture → lexer/parser → expander → exécution (processus/FD).
  • Maîtriser les concepts systèmes : fork/exec/wait, pipe, dup2, redirections, signaux.
  • Produire un programme stable : pas de crash, gestion propre des erreurs, pas de leaks (hors cas connus de readline).

Fonctionnement

En lançant ./minishell, un prompt apparaît :

  • La saisie est gérée via readline (historique, édition de ligne).
  • La ligne est analysée (tokens, quotes, opérateurs).
  • Les expansions sont appliquées ($VAR, $?, et comportement des quotes).
  • Le shell exécute :

  • soit un builtin dans le process du shell (ex : cd, export…)

  • soit un binaire externe via fork() + execve()
  • Les redirections et pipes sont branchés avec dup2() / pipe().

Features clés

Parsing & quoting

  • Gestion des quotes simples ('...') : protège tout, aucune expansion.
  • Gestion des doubles quotes ("...") : protège les métacaractères mais garde l’expansion $.
  • Refus / gestion d’erreurs sur les syntaxes non supportées par le sujet (ex. quotes non fermées).

Redirections & pipes

  • Redirections :

  • < input

  • > output (truncate)
  • >> output (append)
  • << heredoc (délimiteur)
  • Pipes | : chaîner plusieurs commandes en connectant stdout → stdin.

Expansions

  • Expansion des variables d’environnement : $VAR → valeur.
  • Expansion du code retour : $? → exit status de la dernière commande.

Builtins

Implémentation des commandes intégrées (selon sujet) :

  • echo (avec -n)
  • cd
  • pwd
  • export
  • unset
  • env
  • exit

Signaux (comportement “bash-like”)

  • Ctrl-C : interrompt et ré-affiche un prompt propre.
  • Ctrl-D : EOF → quitte le shell.
  • Ctrl-\ : comportement conforme au sujet (pas de quit sauvage en interactive).

Rôle dans le projet (groupe)

Sur ce projet de groupe, je me suis principalement occupé de :

  • Parsing : Build de l'ast qui servira a prioriser les operation utiliser dans l'execution
  • Builtins : implémentation et intégration des commandes internes (ex. cd, export, unset, echo, pwd, env, exit), avec un comportement cohérent côté environnement et codes de retour.
  • Testing & debug : création de cas de test (syntaxes, quotes, expansions, erreurs), reproduction de bugs, et triage pour identifier rapidement si le problème venait de mon périmètre (parsing/builtins) ou de celui de ma collègue (exec/FD/signaux, etc.).

Ce que ça m’a apporté

minishell m’a fait passer un cap “systèmes” : raisonner en termes de process graph, de flux (stdin/stdout), d’exit status, et d’edge cases réalistes. C’est aussi un excellent projet de collaboration : découpage des responsabilités, conventions d’équipe, et intégration de modules (parsing, env, exec, signals).

Bonus

Note : dès le début, notre AST était conçu pour supporter des extensions de grammaire. Même si toutes n’étaient pas exigées/activées dans la version rendue, la structure permettait d’ajouter proprement des opérateurs supplémentaires (ex. &&, ||, parenthèses/subshell).

  • Extensions de grammaire (ex : &&, ||, parenthèses/subshell)
  • Amélioration des diagnostics (messages d’erreur plus proches de bash)
  • Optimisations (moins de forks inutiles, meilleure gestion des builtins)

Lancement

./minishell

Lecture de ligne

  • Prompt interactif via readline
  • Historique de commandes
  • EOF (Ctrl-D) : quitte le shell

Builtins (implémentés)

  • echo (option -n)
  • cd
  • pwd
  • export
  • unset
  • env
  • exit

Expansions

  • $VAR → valeur de l’environnement
  • $? → code retour de la dernière commande
  • Quotes :

  • '...' : pas d’expansion

  • "..." : expansion $ autorisée

Pipes & redirections

  • Pipes : cmd1 | cmd2 | cmd3 ...
  • Redirections :

  • < input

  • > output (truncate)
  • >> output (append)
  • << heredoc (délimiteur)

Signaux (comportement attendu)

  • Ctrl-C : nouveau prompt propre
  • Ctrl-\ : conforme au sujet (pas de quit brutal en interactif)

Pipeline global

  1. readline → récupère la ligne
  2. lexer/tokenizer → découpe en tokens (mots, opérateurs, quotes)
  3. parser → construit une représentation exploitable (AST)
  4. expander → applique $VAR / $? selon les règles de quotes
  5. executor → gère redirections, pipes, fork/exec, builtins

AST dès le départ

Notre architecture était pensée dès le début autour d’un AST afin de :

  • séparer clairement parsing et exécution
  • gérer naturellement la priorité des opérateurs (pipes, redirections, etc.)
  • préparer les extensions de grammaire (ex. &&, ||, parenthèses/subshell) sans casser le code

Ton périmètre (groupe)

  • Parsing / quoting : tokens, validation, construction de commandes/args
  • Builtins : implémentation + intégration dans le pipeline
  • Testing : création de cas limites + triage des bugs (parsing/builtins vs exec/FD/signaux)

Points critiques

  • Les builtins comme cd/export/unset/exit doivent souvent s’exécuter dans le process du shell (sinon l’état ne persiste pas).
  • La gestion des fd (pipes/redirs) doit être propre : fermeture stricte pour éviter blocages.
  • readline + signaux : maintenir un prompt propre, sans laisser le shell dans un état incohérent.