diff options
| -rw-r--r-- | main.c | 38 | ||||
| -rw-r--r-- | project.md | 123 |
2 files changed, 139 insertions, 22 deletions
@@ -4,6 +4,9 @@ #include <readline/readline.h> #include <dirent.h> #include <unistd.h> +#include <readline/readline.h> +#include <readline/history.h> + /* BOOLEANS */ #define true 1 @@ -14,38 +17,30 @@ typedef struct int process_id; } id; -void process_execution(int id) -{ - fork(); -} -void REDIRECTION(); -void SCRIPTING(); - -void ERROR_QUIT(char *CURRENT_COMMAND) -{ - printf("failed %s", CURRENT_COMMAND); - -} - -void err_sys(char *string) +void exit_program(char *argv) { - printf("%s", string); + if (strcmp(argv, "exit")) + { + exit(0); + } } -void PROCESS_PID() +void get_pid_parent_process() { - printf("PID %ld\n", (long)getpid()); } void list_directories() { + } + char *save_variable(char* command, char *argv) { char *variable = (char *)malloc(sizeof(char)); + char *input = readline(); if (strcmp(argv[0], "$") == 0) { @@ -56,7 +51,7 @@ char *save_variable(char* command, char *argv) } } -void ECHO_INPUT(char *command, char *argv) +void echo(char *command, char *argv) { if (strcmp(command, "echo") == 0) { @@ -64,10 +59,9 @@ void ECHO_INPUT(char *command, char *argv) } } -int main(int argc, char *argv[]) { - - int i = getpid(); - printf("%d", i); +int main(int argc, char *argv[]) +{ + return 0; } 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? 🚀 |
