Minishell is a project from the 42 school curriculum that consists of creating a simple shell, similar to bash. The main goal is to understand the inner workings of a shell, including process creation, file descriptors, pipes, redirections, and environment variables.
This document provides a detailed explanation of the project's structure, implementation, and features.
- Command Execution: Executes simple and complex commands with arguments.
- Pipes: Supports multiple pipes (e.g.,
ls -l | grep minishell | wc -l). - Redirections:
- Input redirection:
< - Output redirection:
> - Append output redirection:
>> - Here document:
<<
- Input redirection:
- Environment Variables:
- Expansion of environment variables (e.g.,
$HOME,$PATH). - Correct handling of
$?to get the exit status of the last command.
- Expansion of environment variables (e.g.,
- Built-in Commands:
echowith the-noption.cdwith relative or absolute paths.pwdto print the current working directory.exportto manage environment variables.unsetto remove environment variables.envto print the environment variables.exitto terminate the shell.
- Signal Handling:
Ctrl-C: Interrupts the current process and displays a new prompt.Ctrl-D: Exits the shell.Ctrl-\\: Does nothing.
- Syntax Validation: Checks for syntax errors before executing commands.
- Unclosed Quotes: Handles unclosed single and double quotes.
To compile the project, simply run the make command in the root directory:
makeThis will create the minishell executable.
To run the shell, execute the following command:
./minishellThe project is organized into the following directories and files:
includes/: Contains the header files for the different modules of the project.minishell.h: Main header file with the primary data structures.parser.h: Header file for the parsing module.execution.h: Header file for the execution module.redirections.h: Header file for the redirections module.builtins.h: Header file for the built-in commands.types.h: Type definitions for the main structures.
srcs/: Contains the source code for the project, organized into subdirectories.minishell.c: The main file with the entry point and the main loop of the shell.parser/: Contains the code for parsing the input line, including tokenization, variable expansion, and quote handling.execution/: Contains the code for executing commands, managing processes, and handling pipes.redirections/: Contains the code for handling input and output redirections.builtins/: Contains the implementation of the built-in commands.
libft/: Contains a custom library with utility functions.Makefile: The makefile for compiling the project.
The main loop of the shell is in the main function in srcs/minishell.c. It performs the following steps in a continuous loop:
- Read Input: Reads a line from the user using the
readlinelibrary. - Handle Unclosed Quotes: Checks for unclosed quotes and prompts the user for more input if necessary.
- Tokenization: The input line is broken down into a list of tokens. Each token has a type (e.g., word, pipe, redirection) and a value.
- Syntax Validation: The list of tokens is checked for syntax errors (e.g., a pipe at the beginning of the line).
- Parsing: The tokens are processed to create a list of commands to be executed. This includes expanding environment variables and handling quotes.
- Execution: The commands are executed. This may involve creating child processes, setting up pipes, and handling redirections.
- Cleanup: Memory allocated for the current command is freed.
The parsing module is responsible for converting the raw input line into a structured representation that can be executed.
- Tokenization: The
tokenizefunction insrcs/parser/tokenize.csplits the input line into tokens. It recognizes words, pipes (|), and redirection operators (<,>,<<,>>). - Variable Expansion: The
expand_wordfunction insrcs/parser/expand.chandles the expansion of environment variables (e.g.,$HOME). - Quote Handling: The shell correctly handles single and double quotes. Characters inside single quotes are treated literally, while variables are expanded inside double quotes.
The execution module is responsible for running the parsed commands.
- Command Creation: The
create_cmdsfunction insrcs/execution/create_cmds.ccreates a list oft_cmdsstructures, where each structure represents a simple command. - Process Management: For external commands, the shell creates a child process using
fork. The child process then executes the command usingexecve. - Pipes: If the command contains pipes, the shell creates a pipe for each
|and connects the standard output of one command to the standard input of the next. - Built-in Commands: Built-in commands are executed directly in the main process, except when they are part of a pipeline.
The redirections module handles input and output redirections.
- File Descriptors: The shell manipulates file descriptors to redirect the input and output of commands.
- Here Documents: For here documents (
<<), the shell reads input from the user until a delimiter is found and stores it in a temporary file. This file is then used as the standard input for the command.
t_geral: The main structure that holds all the important data for the shell, such as the environment variables, the list of tokens, and the list of commands.t_token: Represents a token from the input line. It has a type, a value, and information about quotes.t_cmds: Represents a simple command. It contains the command arguments, the path to the executable, and the file descriptors for input and output.t_env: A linked list that stores the environment variables.
echo [-n]: Prints its arguments to the standard output. The-noption suppresses the trailing newline.cd [path]: Changes the current directory.pwd: Prints the current working directory.export [name[=value]] ...: Sets environment variables. If no arguments are given, it prints a list of all environment variables.unset [name] ...: Removes environment variables.env: Prints the environment variables.exit [n]: Exits the shell with an optional exit status.
The shell has a robust error handling mechanism. It prints clear error messages to the standard error stream when something goes wrong, such as:
- Command not found.
- No such file or directory.
- Permission denied.
- Syntax errors.
The shell also sets the exit status ($?) appropriately after each command.