Skip to content

Minishell project 42

Ilaria Nassi edited this page Feb 5, 2026 · 1 revision

🐚 Minishell – panoramica

“Minishell è una shell semplificata che replica il comportamento base di bash: legge un input, lo tokenizza, lo analizza, costruisce i comandi e li esegue gestendo builtin, segnali, redirections e pipeline.”

🧩 Token & Parser

“Il lexer divide l’input in token, che rappresentano le unità minime come parole e operatori. Il parser usa questi token per costruire una lista di comandi (t_cmd), ognuno con argomenti e redirections.”

🔗 Builder

“Il builder fa da ponte tra parser ed execution: converte le strutture del parser in strutture più semplici e lineari, pronte per essere eseguite, come t_exec_cmd e t_pipeline.”

🧠 Builtin

“Le builtin sono comandi interni alla shell. Quelle che modificano lo stato della shell, come cd, export, unset ed exit, devono essere eseguite nel processo parent. Se invece una builtin è in una pipeline, viene eseguita in un child, come in bash.”

🔁 Parent / Child

“Il parent è il processo shell che mantiene lo stato globale, mentre i child sono processi temporanei creati con fork per eseguire i comandi. Le modifiche fatte dai child non tornano al parent.”

🔀 Execution

“La fase di execution gestisce fork, execve, pipe, redirections ed heredoc. Se c’è una pipeline, ogni comando viene eseguito in un processo separato e collegato tramite pipe. L’exit code finale è quello dell’ultimo comando, come in bash.”

📄 Redirections & enum

“Le redirections sono rappresentate con un enum (R_IN, R_OUT, R_APP, R_HEREDOC) per distinguere chiaramente i diversi tipi di input e output e gestirli con switch-case.”

🧠 Heredoc

“Gli heredoc vengono preparati prima dell’esecuzione: il contenuto viene letto, scritto su un file temporaneo e poi trattato come un normale input file.”

🚦 Signals

“La gestione dei segnali cambia in base al contesto: nel prompt SIGINT non chiude la shell, durante l’esecuzione i segnali colpiscono solo i child, garantendo un comportamento coerente con bash.”

📦 BUILDER – collegamento parser → execution

Builder fa da ponte tra il parser e l’esecuzione. Il parser produce una struttura comoda per analizzare la sintassi, ma non ideale per l’esecuzione. Il builder converte questi dati in strutture più semplici e lineari, pronte per fork, pipe e redirections.

In pratica cosa fa il builder:

Trasforma t_cmd (output del parser) in:

t_exec_cmd → argv + redirections

t_pipeline → array di comandi + numero totale

Duplica in modo sicuro:

argv

liste di redirections

Mantiene l’ordine delle redirections (fondamentale: l’ultima vince)

Fornisce funzioni di free centralizzate, per evitare memory leak

Punto tecnico che puoi sottolineare

“Il builder separa completamente parsing ed execution: execution non conosce token o sintassi, ma solo comandi già pronti.”

🧩 BUILTIN – comandi interni alla shell

Builtin, cioè quei comandi che non possono essere eseguiti con execve perché devono modificare lo stato della shell.

Builtin principali

cd → cambia directory del processo shell

export / unset → modificano l’ambiente

exit → termina la shell

echo, pwd, env → output standard

“Le builtin vengono eseguite nel parent solo quando non sono in pipeline, perché altrimenti le modifiche andrebbero perse nel child.”

In pipeline → builtin nel child (comportamento bash)

Fuori pipeline → builtin nel parent, ma:

salvo stdin/stdout

applico redirections

ripristino i fd dopo l’esecuzione

Altro punto tecnico forte

Gestione exit code coerente con bash

Validazione identificatori (export, unset)

Gestione errori (exit con argomenti non numerici, troppi argomenti, ecc.)

🚦 SIGNALS – comportamento interattivo corretto

Gestione dei segnali, fondamentale per rendere la shell utilizzabile come bash.

Comportamenti implementati

Prompt interattivo

Ctrl-C:

non chiude la shell

pulisce la riga corrente

ristampa il prompt

Durante l’esecuzione

il parent ignora SIGINT/SIGQUIT

i child ripristinano il comportamento di default

Heredoc

Ctrl-C interrompe correttamente l’input heredoc

Frase molto efficace

“La gestione dei segnali cambia in base al contesto: prompt, child, parent in esecuzione. Questo evita che la shell muoia quando si interrompe un comando.”

Mi sono occupata dei moduli che collegano parsing, esecuzione e comportamento interattivo: il builder prepara i comandi, le builtin gestiscono lo stato della shell, i segnali garantiscono un comportamento coerente con bash, e l’execution coordina fork, pipe, redirections e heredoc.

1 Builder → ponte parser/execution

2 Builtin → comandi che modificano la shell, parent vs child

3 Signals → comportamento corretto di Ctrl-C / Ctrl-\

4 Execution → fork, pipe, redirections, heredoc, exit code

****BUILTIN: ****

🔹 cd – change directory Cosa fa

Cambia la directory di lavoro del processo shell.

Senza argomenti → va in $HOME

Con un path → prova a spostarsi lì

Aggiorna le variabili:

PWD

OLDPWD

Perché è una builtin

Perché chdir() cambia la directory solo del processo che la chiama.

Se cd fosse eseguito con execve in un child:

il child cambierebbe directory

il parent (la shell) resterebbe nella directory precedente ➡️ comportamento sbagliato

“cd è una builtin perché deve modificare lo stato del processo shell; se fosse eseguita in un child, il cambio di directory andrebbe perso.”

🔹 export – aggiunge/modifica variabili d’ambiente

Senza argomenti → stampa le variabili d’ambiente in ordine alfabetico

Con argomenti:

export VAR=value → crea o modifica la variabile

export VAR → la esporta senza valore

Controlli importanti

Il nome della variabile deve:

iniziare con lettera o _

contenere solo alfanumerici o _

Se non valido → errore, ma la shell non termina

Perché è una builtin

Perché modifica l’ambiente della shell.

Un execve può solo leggere l’ambiente, non modificarlo per il parent.

“export gestisce l’ambiente della shell, quindi deve essere eseguita nel parent per rendere le variabili disponibili ai comandi successivi.”

🔹 unset – rimuove variabili d’ambiente

Rimuove una o più variabili dall’ambiente:

unset VAR

Regole

Stesse regole di validazione di export

Se la variabile non esiste → nessun errore

Perché è una builtin

Perché modifica direttamente l’ambiente della shell.

“unset elimina variabili dall’ambiente della shell e quindi deve essere eseguita nel processo principale.”

🔹 exit – termina la shell

Senza argomenti → termina la shell con exit code corrente

Con argomento numerico → termina con quel valore (mod 256)

Con argomento non numerico:

stampa errore

exit code 2

Con troppi argomenti:

stampa errore

non termina la shell

Perché è una builtin

Perché deve terminare la shell stessa, non un processo figlio.

Se fosse in un child → la shell continuerebbe a girare.

“exit è una builtin perché deve interrompere il processo shell e gestire correttamente i codici di uscita come bash.”

🔹 echo – stampa su standard output

Stampa gli argomenti

Supporta l’opzione:

-n → non stampa il newline finale

Esempio:

echo -n ciao

Perché è una builtin

In bash è una builtin per efficienza e compatibilità

In minishell:

non modifica lo stato della shell

può essere eseguita anche in pipeline

echo è una builtin semplice: gestisce solo output, ma deve rispettare il comportamento di bash, come l’opzione -n.

🔹 pwd – print working directory

Stampa la directory corrente

Implementazione

Usa getcwd() oppure il valore di PWD

Perché è una builtin

In bash è builtin

In minishell è semplice e veloce

Frase breve

“pwd stampa la directory corrente della shell.”

🔹 env – stampa l’ambiente

Stampa tutte le variabili d’ambiente nel formato:

VAR=value

Solo quelle con valore assegnato

Nota importante

Non stampa variabili senza =

Non modifica nulla

Perché è una builtin

Accesso diretto all’ambiente della shell

“env stampa l’ambiente corrente, ed è utile per verificare le variabili esportate.”

Builtin + pipeline e discorso parent / child Cos’è una pipeline

Una pipeline è una sequenza di comandi collegati con |:

ls | grep txt | wc -l

Ogni comando:

legge da stdin

scrive su stdout

lo stdout di uno diventa lo stdin del successivo

👉 Per funzionare, ogni comando della pipeline deve stare in un processo diverso.

Builtin in pipeline

Esempio:

export VAR=42 | cat

In bash:

export viene eseguito in un child

la variabile non rimane nella shell

Regola importante

Se una builtin è dentro una pipeline, viene eseguita in un child.

Questo vale anche per:

cd

export

unset

exit

Builtin NON in pipeline export VAR=42 cd src

Qui:

la builtin viene eseguita nel parent

modifica lo stato della shell

“Le builtin vengono eseguite nel parent solo se non fanno parte di una pipeline; in pipeline devono stare in un child per rispettare il modello dei processi.”

🧠 Parent e Child: concetto base Parent

È il processo minishell

Mostra il prompt

Mantiene:

directory corrente

ambiente

exit status

Child

Creato con fork()

Esegue:

comandi esterni

builtin in pipeline

Muore dopo l’esecuzione

👉 Tutto ciò che il child modifica non torna al parent.

🏠 Cos’è $HOME

$HOME è una variabile d’ambiente.

Contiene:

/home/nome_utente

A cosa serve

Indica la directory “personale” dell’utente

Usata da:

cd (senza argomenti)

programmi che salvano config

“$HOME è una variabile d’ambiente che indica la directory personale dell’utente.”

🐚 Cos’è bash

Bash è una shell, cioè un programma che:

legge comandi da tastiera

li interpreta

li esegue

Esempi di shell

bash

zsh

fish

minishell (la vostra!)

Perché si cita bash

È lo standard di riferimento

Minishell deve comportarsi “come bash”:

exit code

pipeline

segnali

builtin

“Bash è la shell di riferimento: minishell cerca di riprodurne il comportamento di base.”

🚦 SIGINT e SIGQUIT

Sono segnali UNIX, usati per comunicare con i processi.

🔹 SIGINT (Interrupt) Come si genera

Ctrl-C

Comportamento standard

Termina il processo

In minishell

Nel prompt:

non chiude la shell

pulisce la riga

ristampa il prompt

Durante l’esecuzione:

colpisce il child

il parent resta vivo

“SIGINT interrompe il comando in esecuzione, ma non deve chiudere la shell.”

🔹 SIGQUIT (Quit) Come si genera

Ctrl-\

Comportamento standard

Termina il processo

produce un core dump

In minishell

ignorato nel prompt

ripristinato nei child

“SIGQUIT viene ignorato dalla shell ma lasciato attivo nei processi figli.”

token Token = unità minima del comando

Esempio:

echo "ciao" > file

Token:

echo → WORD

"ciao" → WORD

→ REDIR_OUT

file → WORD

A cosa servono

Il lexer divide l’input in token

Il parser usa i token per costruire i comandi

“I token sono i mattoni di base dell’input, prodotti dal lexer e usati dal parser.”

Enum redirections enum e_redir_type { R_IN, // < R_OUT, // > R_APP, // >> R_HEREDOC // << };

Cosa rappresentano

R_IN → input da file

R_OUT → output su file (truncate)

R_APP → output in append

R_HEREDOC → input multilinea da stdin

Perché usare un enum

codice più leggibile

evita stringhe o numeri magici

facile switch-case

“Le redirections sono rappresentate con un enum per distinguere chiaramente i diversi tipi di input e output.” Minishell replica il comportamento di bash: tokenizza l’input, costruisce i comandi, gestisce builtin, segnali, redirections e pipeline usando fork e pipe, distinguendo tra parent e child per preservare lo stato della shell.