https://github.com/mathyscogne/42_minishell
MiniShell: A custom Unix shell developed in C. Supports pipes, redirections, built-in commands (cd, echo, etc.), environment variable expansion, and error handling.
https://github.com/mathyscogne/42_minishell
42 c minishell
Last synced: 10 months ago
JSON representation
MiniShell: A custom Unix shell developed in C. Supports pipes, redirections, built-in commands (cd, echo, etc.), environment variable expansion, and error handling.
- Host: GitHub
- URL: https://github.com/mathyscogne/42_minishell
- Owner: MathysCogne
- Created: 2024-12-12T18:20:31.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-01-15T17:13:06.000Z (over 1 year ago)
- Last Synced: 2025-03-13T04:14:14.101Z (about 1 year ago)
- Topics: 42, c, minishell
- Language: C
- Homepage:
- Size: 2.01 MB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
## Features
- **Prompt**: Displays a prompt when waiting for a new command.
- **History**: Keeps a history of commands.
- **Pipes**: Supports pipes (`|`) to connect multiple commands.
- **Redirections**: Handles redirections (`>`, `>>`, `<`, `<<`) seamlessly.
- **Environment Variables**: Expands environment variables (`$HOME`, `$PATH`, etc.).
- **Error Codes**: Tracks and returns error codes (`$?`) for executed commands.
- **Builtins**: Implements internal commands:
- `echo` (with `-n` flag)
- `cd` (change directory)
- `pwd` (print working directory)
- `export` (set environment variables)
- `unset` (unset environment variables)
- `env` (display environment variables)
- `exit` (terminate the shell)
- **Signals**: Properly handles signals such as:
- `Ctrl+C` (interrupts the current command and displays a new prompt)
- `Ctrl+D` (exits the shell if pressed at an empty prompt)
- `Ctrl+\` (ignored in interactive mode).
## Parsing Part
The parsing stage is the backbone of the shell, as it transforms the raw input into a structured format for execution. It consists of two main steps: **tokenization** and **semantic analysis**.
### Tokenization
The tokenization process splits the user's input into tokens, which are the smallest meaningful units of the command. These tokens include:
- **Commands** (e.g., `ls`, `cat`, `grep`)
- **Arguments** (e.g., `-l`, `file.txt`)
- **Redirections** (`>`, `>>`, `<`, `<<`)
- **Pipes** (`|`)
#### How Tokenization Works:
1. **Identify the components**: The input string is traversed character by character to identify commands, arguments, redirections, and special characters like `|`.
2. **Handle quotes**: Properly handles single (`'`) and double (`"`) quotes to preserve spaces or special characters inside quoted strings.
- Single quotes prevent all expansions.
- Double quotes allow variable expansions (`$`).
3. **Ignore whitespace**: Skips unnecessary spaces while separating meaningful components.
4. **Classify tokens**: Assigns a type to each token (e.g., `TOKEN_COMMAND`, `TOKEN_ARGUMENT`, `TOKEN_PIPE`, etc.).
Input:
```bash
echo "hello world" | grep hello > output.txt
```
Tokens generated:
```bash
[TOKEN_COMMAND: echo]
[TOKEN_ARGUMENT: "hello world"]
[TOKEN_PIPE: |]
[TOKEN_COMMAND: grep]
[TOKEN_ARGUMENT: hello]
[TOKEN_REDIRECTION_OUT: >]
[TOKEN_ARGUMENT: output.txt]
```
### **2. Semantic Analysis**
The semantic analysis phase transforms the list of tokens into a hierarchy of commands and redirections while validating the syntax. This ensures that the input is both logical and executable.
#### Steps of Semantic Analysis:
1. **Validate Syntax**:
- Detect invalid sequences like `| |`, `; ;`, or missing arguments for redirections (`>`, `<`).
- Example: `echo |` will raise an error.
2. **Group Tokens**:
- Constructs a command tree where each node represents a command or redirection.
- Associates redirections (`>`, `<`, etc.) with their respective commands.
3. **Handle Logical Constructs**:
- Maps pipes (`|`) to connect commands in a pipeline.
Input:
```bash
cat file.txt | grep "hello" > result.txt
```
Command structure:
```yaml
Command 1:
- Executable: cat
- Arguments: [file.txt]
Pipe to Command 2:
Command 2:
- Executable: grep
- Arguments: ["hello"]
- Redirection: > result.txt
```
## Execution Part
Once the input is parsed and analyzed, the shell proceeds to the execution phase. Commands are executed in separate processes, with builtins handled directly in the shell process.
### Execution Flow:
1. **Forking**:
- Creates a new process using `fork()` for each external command.
- The parent process waits for the child process to complete using `waitpid()`.
2. **Piping**:
- Sets up pipes (`pipe()`) to connect the output of one command to the input of the next.
- Uses `dup2()` to duplicate file descriptors for standard input/output redirection.
3. **Redirections**:
- Opens file descriptors for input/output redirections (`<`, `>`, `>>`, `<<`).
- Uses `dup2()` to redirect standard input/output to the appropriate file descriptors.
4. **Builtin Commands**:
- Executes builtins directly without forking to avoid unnecessary processes.
- Builtins are handled by specific functions within the shell process.
## Compilation and Usage
```bash
# Compile:
make
# Start shell:
./minishell
# Start shell with test (Norm and Valgrind):
make test
```
⊹ ࣪ ﹏𓊝﹏𓂁﹏⊹ ࣪ ˖
## Disclaimer
> At 42 School, most projects must comply with the [Norm](https://github.com/42School/norminette/blob/master/pdf/en.norm.pdf).