7.7 KiB
Minishell - Guia de estructura y pasos (parte obligatoria)
Objetivo de este documento
- Proponer una estructura de archivos y datos.
- Describir el proceso paso a paso como tutorial.
- Servir de guion para defensa (explicaciones claras y ordenadas).
Este documento no implementa nada. Solo define el plan y el por que.
1. Vision general del flujo
El shell se puede explicar como una tuberia de fases:
- Lectura interactiva (prompt + historial).
- Tokenizacion (lexing) con comillas y metacaracteres.
- Parseo (construccion de comandos, redirecciones, pipes).
- Expansion de variables (
VAR y?). - Preparacion de ejecucion (resolucion de rutas, heredoc).
- Ejecucion (builtins o execve) con pipes y redirecciones.
- Gestion de exit status y señales.
Esta separacion permite explicar en defensa cada pieza por separado y justificar las decisiones tecnicas.
2. Propuesta de estructuras de datos
Estas estructuras son solo guia. Adapta nombres a tu estilo y Norminette.
2.1 Token (lexer)
Representa una unidad del input (palabra, pipe, redireccion, etc.).
-
enum e_tokentype
- TOK_WORD
- TOK_PIPE
- TOK_REDIR_IN (<)
- TOK_REDIR_OUT (>)
- TOK_REDIR_APPEND (>>)
- TOK_HEREDOC (<<)
-
struct s_token
- char *text
- t_tokentype type
- struct s_token *next
Uso en defensa: el lexer separa el input en unidades, respetando comillas.
2.2 Redireccion
Guarda los datos de redireccion por comando.
-
enum e_redirtype
- REDIR_IN
- REDIR_OUT
- REDIR_APPEND
- REDIR_HEREDOC
-
struct s_redir
- t_redirtype type
- char *target
- int fd
- struct s_redir *next
Notas:
- target es el filename o delimitador de heredoc.
- fd se resuelve en fase de preparacion (open o pipe temporal).
2.3 Comando
- struct s_command
- char **argv
- int argc
- char *path
- t_redir *redirs
2.4 Pipeline
Una lista de comandos en orden, unidos por pipes.
- struct s_pipeline
- t_command **cmds
- size_t count
3. Lexer: reglas y pasos
Reglas clave del subject:
- No interpretar comillas sin cerrar.
- Comilla simple: no se expanden variables ni metacaracteres.
- Comilla doble: se expanden variables, pero se respetan caracteres normales.
- Metacaracteres: |, <, >, <<, >> separan tokens.
Pasos recomendados:
- Recorrer la linea caracter a caracter.
- Mantener estado: in_single, in_double.
- Cuando no estas en comillas, detectar metacaracteres y cortar tokens.
- Construir TOK_WORD con el texto exacto (sin eliminar comillas aun).
- Si llegas a fin de linea con in_single o in_double activo, error de parseo.
Explicacion para defensa:
- El lexer no sabe de ejecucion, solo separa en tokens validos.
- El manejo de comillas se hace aqui para respetar la sintaxis del shell.
4. Parser: construccion de comandos
Objetivo: transformar tokens en una estructura ejecutable.
Pasos:
- Recorrer lista de tokens.
- Cada TOK_PIPE cierra un comando actual y abre el siguiente.
- TOK_WORD se agrega a argv.
- TOK_REDIR_* consume el siguiente token (debe ser TOK_WORD) como target.
- Construir lista de redirecciones para cada comando.
- Validar errores: pipe inicial/final, redireccion sin target, etc.
Explicacion para defensa:
- El parser aplica reglas de orden y construye una estructura clara.
- Separar argv y redirecciones evita mezclar logica en executor.
5. Expansion de variables
Reglas:
- $VAR se sustituye por getenv/tabla interna.
- $? se sustituye por el exit_status anterior.
- En comilla simple no se expande.
- En comilla doble si se expande.
Proceso recomendado:
- Durante tokenizacion, guardar el texto con sus comillas o bien marcar segmentos con estado de comillas.
- En expansion, recorrer cada palabra y reemplazar $...
- Si variable no existe, reemplazar por string vacio.
- Eliminar comillas despues de la expansion.
Explicacion para defensa:
- La expansion es una fase separada para no complicar el parser.
- $?, variable especial, refleja el estado de la ultima ejecucion.
6. Redirecciones y heredoc
Redirecciones basicas:
- <: open(file, O_RDONLY)
-
: open(file, O_WRONLY | O_CREAT | O_TRUNC)
-
: open(file, O_WRONLY | O_CREAT | O_APPEND)
Heredoc (<<):
- Leer lineas hasta delimitador exacto.
- Guardar el contenido en un pipe o fichero temporal.
- Usar el extremo de lectura como STDIN del comando.
- No guardar el contenido en historial.
Explicacion para defensa:
- Las redirecciones se resuelven antes de ejecutar el proceso.
- Heredoc es una fuente especial de entrada.
7. Resolucion de comandos y PATH
Reglas:
- Si argv[0] es una ruta absoluta o relativa (/, ./, ../), usarla tal cual.
- Si no, buscar en PATH separando por ':'.
- Si es builtin, no se necesita path real.
Proceso:
- Detectar builtin.
- Si no builtin y no es ruta, recorrer PATH y usar access().
- Guardar path en t_command->path.
8. Ejecucion
Caso 1: comando unico builtin
- Ejecutar en el proceso padre para que pueda modificar estado del shell (ej: cd, export, unset, exit).
Caso 2: pipeline o comando externo
- Usar fork + execve.
- Crear pipes entre comandos.
- Aplicar redirecciones antes de ejecutar.
Proceso para pipeline:
- Para cada comando, crear pipe si hay siguiente.
- fork.
- En child: dup2 para redirecciones y pipes, luego ejecutar.
- En parent: cerrar FDs innecesarios y seguir.
- Esperar procesos, guardar exit status del ultimo comando.
Explicacion para defensa:
- Las pipes conectan stdout del comando i con stdin del comando i+1.
- Los builtins dentro de pipeline se ejecutan en child.
9. Builtins obligatorios
- echo con -n
- cd (ruta relativa o absoluta)
- pwd
- export (sin opciones)
- unset (sin opciones)
- env (sin opciones o argumentos)
- exit
Notas de defensa:
- export/unset trabajan sobre la tabla de variables del shell.
- env imprime variables de entorno.
- exit debe actualizar exit_status y terminar el loop principal.
10. Senales
Requisitos interactivos:
- ctrl-C: imprime nueva linea y muestra prompt.
- ctrl-D: termina el shell.
- ctrl-: no hace nada.
Regla del subject:
- Solo una variable global para indicar la senal recibida.
Proceso:
- Definir una variable global int g_signal.
- Configurar handlers con sigaction.
- En handler: actualizar g_signal y escribir un '\n' si procede.
- En el loop principal: si g_signal indica SIGINT, resetear lineas de readline.
11. Manejo de errores y salida
- Mostrar errores con perror o mensajes consistentes.
- Si parseo falla, no ejecutar nada.
- Mantener exit_status actualizado.
Explicacion en defensa:
- Un shell robusto evita ejecutar comandos parcialmente parseados.
- exit_status es clave para $?.
12. Checklist para defensa (guion rapido)
- Explico el flujo completo: lectura -> lexer -> parser -> expansion -> exec.
- Explico como manejo comillas y metacaracteres.
- Explico como construyo argv y redirecciones.
- Explico expansion de
VAR y?. - Explico pipes y redirecciones con dup2.
- Explico por que los builtins se ejecutan en parent o child.
- Explico manejo de senales y la variable global unica.
- Explico exit_status y comportamiento de $?.
13. Sugerencia de estructura de archivos
-
include/
- minishell.h
- core.h (estructuras globales y estado)
- parser.h (tokens, parser)
- executor.h
- builtins.h
-
src/
- core/ (init, signals, util)
- parser/ (lexer.c, parser.c, expand.c)
- executor/ (executor.c, redirs.c)
- builtins/ (echo, cd, pwd, exit, env, export, unset)
- variables/ (environment.c)
- minishell.c (loop principal)
- main.c
Esto es solo una guia; no es obligatorio seguirla al pie de la letra.
14. Consejos para la defensa
- Usa bash como referencia de comportamiento.
- Demuestra un par de ejemplos: pipe, redireccion y expansion.
- Si algo falla, explica que el parser previene ejecucion parcial.
- Recalca el manejo correcto de ctrl-C y ctrl-.