Files
minishell/docs/guia_obligatoria_es.md
2026-02-09 20:47:43 +01:00

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:

  1. Lectura interactiva (prompt + historial).
  2. Tokenizacion (lexing) con comillas y metacaracteres.
  3. Parseo (construccion de comandos, redirecciones, pipes).
  4. Expansion de variables (VAR y ?).
  5. Preparacion de ejecucion (resolucion de rutas, heredoc).
  6. Ejecucion (builtins o execve) con pipes y redirecciones.
  7. 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:

  1. Recorrer la linea caracter a caracter.
  2. Mantener estado: in_single, in_double.
  3. Cuando no estas en comillas, detectar metacaracteres y cortar tokens.
  4. Construir TOK_WORD con el texto exacto (sin eliminar comillas aun).
  5. 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:

  1. Recorrer lista de tokens.
  2. Cada TOK_PIPE cierra un comando actual y abre el siguiente.
  3. TOK_WORD se agrega a argv.
  4. TOK_REDIR_* consume el siguiente token (debe ser TOK_WORD) como target.
  5. Construir lista de redirecciones para cada comando.
  6. 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:

  1. Durante tokenizacion, guardar el texto con sus comillas o bien marcar segmentos con estado de comillas.
  2. En expansion, recorrer cada palabra y reemplazar $...
  3. Si variable no existe, reemplazar por string vacio.
  4. 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 (<<):

  1. Leer lineas hasta delimitador exacto.
  2. Guardar el contenido en un pipe o fichero temporal.
  3. Usar el extremo de lectura como STDIN del comando.
  4. 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:

  1. Detectar builtin.
  2. Si no builtin y no es ruta, recorrer PATH y usar access().
  3. 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:

  1. Para cada comando, crear pipe si hay siguiente.
  2. fork.
  3. En child: dup2 para redirecciones y pipes, luego ejecutar.
  4. En parent: cerrar FDs innecesarios y seguir.
  5. 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:

  1. Definir una variable global int g_signal.
  2. Configurar handlers con sigaction.
  3. En handler: actualizar g_signal y escribir un '\n' si procede.
  4. 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)

  1. Explico el flujo completo: lectura -> lexer -> parser -> expansion -> exec.
  2. Explico como manejo comillas y metacaracteres.
  3. Explico como construyo argv y redirecciones.
  4. Explico expansion de VAR y ?.
  5. Explico pipes y redirecciones con dup2.
  6. Explico por que los builtins se ejecutan en parent o child.
  7. Explico manejo de senales y la variable global unica.
  8. 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-.