diff options
| author | Abdellah El Morabit <nsrddyn@gmail.com> | 2025-02-02 20:00:50 +0100 |
|---|---|---|
| committer | Abdellah El Morabit <nsrddyn@gmail.com> | 2025-02-02 20:00:50 +0100 |
| commit | d722c601e8a80279027aa2988dc5fcd371ee9d4d (patch) | |
| tree | 02afa9472b80a9a2e1b71b686dbcbc58dfc7defa /project.md | |
| parent | cfee09646864f0e9426a1313250b2fe2e5cc4a84 (diff) | |
layout the plans for making the shell, i understand the idea a little better now, i think i implemented an exit function, pretty sure it works decently well, current next thing im going to work on is understanding readline, the autocompletion that comes with it, and trying to list a directory which i have no clue how im going to do
Diffstat (limited to 'project.md')
| -rw-r--r-- | project.md | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/project.md b/project.md new file mode 100644 index 0000000..d8561cf --- /dev/null +++ b/project.md @@ -0,0 +1,123 @@ +### **Building a Unix Shell from Scratch** +When building a Unix shell from scratch, you typically create a **command-line interface (CLI)** that can: +1. Read user input (commands). +2. Parse the input (tokenizing, handling arguments, redirections, etc.). +3. Execute the command, either internally (built-in commands) or by forking a new process. +4. Handle job control (foreground/background processes, signals). +5. (Optionally) Support scripting, aliases, and advanced features like pipes (`|`) and I/O redirection (`>`, `<`). + +A minimal shell implementation in C follows these steps: + +#### **1. Read User Input** +Use `getline()` or `read()` to accept input from the terminal. + +#### **2. Tokenize Input* +convert into **tokens** (command and arguments) using `strtok()`. + +#### **3. Execute the Command** +- If it is a built-in command (e.g., `cd`, `exit`), handle it directly. +- Otherwise, use `fork()` to create a child process and `execvp()` to execute the command. + +#### **4. Handle Process Management** +- Use `waitpid()` to wait for processes. +- Implement background jobs (`&` support). +- Signal handling (`SIGCHLD`, `SIGINT` for `Ctrl+C`). + +#### **5. Optional Features** +- Environment variables handling (`export`, `$PATH` resolution). +- File redirections (`>` for output, `<` for input). +- Pipe (`|`) execution with `pipe()`, `dup2()`, and multiple processes. + +Here’s a **basic Unix shell** in C: + +```c +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> + +#define MAX_CMD_LEN 1024 +#define MAX_ARGS 64 + +void execute_command(char **args) { + pid_t pid = fork(); + if (pid == 0) { // Child process + execvp(args[0], args); + perror("exec failed"); + exit(1); + } else if (pid > 0) { // Parent process + wait(NULL); + } else { + perror("fork failed"); + } +} + +int main() { + char input[MAX_CMD_LEN]; + char *args[MAX_ARGS]; + + while (1) { + printf("mysh> "); + if (!fgets(input, MAX_CMD_LEN, stdin)) break; // Read input + + input[strcspn(input, "\n")] = 0; // Remove newline + + // Tokenize input + int i = 0; + args[i] = strtok(input, " "); + while (args[i] && i < MAX_ARGS - 1) { + args[++i] = strtok(NULL, " "); + } + args[i] = NULL; // NULL-terminate args + + if (args[0]) { + if (strcmp(args[0], "exit") == 0) break; // Built-in exit + execute_command(args); + } + } + return 0; +} +``` + +This basic shell: +- Reads input +- Tokenizes it +- Forks a process and executes commands using `execvp()` +- Waits for processes to complete +- Supports built-in `exit` command + +A full-featured shell would include: +- **Job control** (`fg`, `bg`, `jobs`) +- **Signal handling** (`SIGTSTP`, `SIGINT`) +- **Redirections** (`>`, `<`) +- **Pipelines** (`|`) +- **Shell scripting** (`if`, `for`, `while` loops) + +--- + +### **Unix Shell vs. POSIX Shell** +A **Unix shell** is a general term for any command interpreter that runs on Unix-like systems (e.g., **bash, zsh, ksh, tcsh, dash**). + +A **POSIX shell** is a shell that strictly conforms to the **POSIX standard (IEEE 1003.1-2008)**, ensuring portability across Unix systems. The **POSIX shell specification** is based on **Bourne Shell (sh)** and defines: +- **Syntax** (`if`, `for`, `case`, `while`, etc.) +- **Built-in commands** (`cd`, `echo`, `export`, `set`, etc.) +- **I/O redirection** (`>`, `<`, `2>`, `&>`, etc.) +- **Substitution** (`$()`, `` ` ``, `${VAR}`) + +**Key Differences:** +| Feature | Unix Shell (Bash, Zsh, etc.) | POSIX Shell (Dash, sh) | +|---------|-----------------|------------------| +| Scripting Extensions | Yes (arrays, `[[ ]]`, associative arrays, `printf -v`, etc.) | No, follows strict POSIX | +| Performance | Slower due to extra features | Faster (used for system scripts) | +| Compatibility | Linux, BSD, Mac, Solaris | Strictly portable | +| Interactive Features | Yes (command history, autocomplete, themes) | Minimal | + +For example: +- **Bash (`bash`)** is a Unix shell but has extra non-POSIX features (`[[` test, associative arrays). +- **Dash (`/bin/dash`)** is a strict POSIX shell, often used for system scripts due to speed. + +If you want to write **portable shell scripts**, stick to **POSIX sh** (e.g., `#!/bin/sh` instead of `#!/bin/bash`). + +Would you like help adding features like redirections or pipes to the basic shell? 🚀 |
