{"id":19476951,"url":"https://github.com/leogaudin/minishell","last_synced_at":"2026-04-30T07:40:04.875Z","repository":{"id":176550228,"uuid":"658641494","full_name":"leogaudin/minishell","owner":"leogaudin","description":"42 • This project is about creating a simple shell. Yes, your own little bash. You will learn a lot about processes and file descriptors.","archived":false,"fork":false,"pushed_at":"2024-03-27T14:07:36.000Z","size":248,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-16T20:47:33.692Z","etag":null,"topics":["42","42born2code","42cursus","42projects","42school","42sh","minishell","minishell42","shell"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leogaudin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-06-26T07:55:31.000Z","updated_at":"2024-12-15T16:47:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"39e5a7ac-4a18-4f90-98f2-2229fb410b91","html_url":"https://github.com/leogaudin/minishell","commit_stats":null,"previous_names":["leogaudin/minishell"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/leogaudin/minishell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leogaudin%2Fminishell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leogaudin%2Fminishell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leogaudin%2Fminishell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leogaudin%2Fminishell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leogaudin","download_url":"https://codeload.github.com/leogaudin/minishell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leogaudin%2Fminishell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32458237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T22:27:22.272Z","status":"online","status_checked_at":"2026-04-30T02:00:05.929Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["42","42born2code","42cursus","42projects","42school","42sh","minishell","minishell42","shell"],"created_at":"2024-11-10T19:42:51.026Z","updated_at":"2026-04-30T07:40:04.836Z","avatar_url":"https://github.com/leogaudin.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\t\u003ch1\u003e🐚 \u003cs\u003eminis\u003c/s\u003e\u003ci\u003ehell\u003c/i\u003e\u003c/h1\u003e\n\t\u003cp\u003eThis project is about creating a simple shell. Yes, your own little bash. You will learn a lot about processes and file descriptors.\u003c/p\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/norminette-pass-success\"/\u003e\n\t\u003ca href=\"https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/leaks-none-success\" /\u003e\n\t\u003c/a\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/bonus-included-success\"/\u003e\n\t\u003cbr /\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/125%2F100-success?logo=42\u0026logoColor=fff\" /\u003e\n\u003c/div\u003e\n\n\u003ch2\u003e⚠️ This project is currently in progress and is neither finished nor tested\u003c/h2\u003e\n\u003ch2\u003e⚠️ Remember that even if there is a nice README with cool emojis, this project has been made by (very good though) students and is probably not perfect\u003c/h2\u003e\n\n\u003ch2\u003e💧 Leaks (dev only)\u003c/h2\u003e\n\n* dev_utils.c: ❓\n* execute_tree.c: ✅\n* executer.c: ❓\n* ft_builtins.c: ❓\n* ft_freefuncs.c: ❓\n* ft_getpathname.c: ❓\n* ft_splitnotstr.c: ❓\n* ft_strjoinfree.c: ❓\n* minishell.c: ❓\n* parse.c: ❓\n* tree.c: ✅\n\n## 📖 Table of Contents\n\u003c!--ts--\u003e\n* [⚙️ Usage](#usage)\n\t* [😫 Troubleshooting](#troubleshooting)\n* [💯 Mandatory part](#mandatory-part)\n\t* [💬 Quotes](#quotes)\n\t* [📈 Expanding variables](#expanding-variables)\n\t* [🔀 Redirections](#redirections)\n\t* [🪈 Pipes](#pipes)\n\t* [🚪 Exit statuses](#exit-statuses)\n\t* [🛠️ Builtins](#builtins)\n\t\t* [`echo`](#echo)\n\t\t* [`cd`](#cd)\n\t\t* [`pwd`](#pwd)\n\t\t* [`export`](#export)\n\t\t* [`unset`](#unset)\n\t\t* [`env`](#env)\n\t\t* [`exit`](#exit)\n\t* [🔔 Signals](#signals)\n\t\t* [`Ctrl-C`](#ctrl-c)\n\t\t* [`Ctrl-D`](#ctrl-d)\n* [🌟 Bonus part](#bonus-part)\n\t* [🌳 `\u0026\u0026` and `||`](#operators)\n\t* [✴ Wildcard expansion](#wildcard-expansion)\n* [🙇🏻 Credits](#credits)\n\u003c!--te--\u003e\n\n## Usage\n\n```bash\ngit clone https://github.com/leogaudin/minishell.git\ncd minishell\nmake \u0026\u0026 ./minishell\n```\n\n### Troubleshooting\n\nIf you have an error when running `make`, it is probably because you do not have the `readline` library installed on your computer, or that it is not well linked.\n\nThere are a few ways to fix this, but ours was:\n\n1. Install vagrant with brew\n\n```bash\nbrew install vagrant\n```\n\n2. Install the `readline` library with brew\n\n```bash\nbrew install readline\n```\n\n3. Create a symlink to the library\n\n```bash\nln /opt/homebrew/Cellar/readline/8.2.1/lib/libreadline.8.dylib /usr/local/lib/libreadline.8.dylib\n```\n\n## Mandatory part\n\n### Quotes\n\n### Expanding variables\n\n### Redirections\n\n### Pipes\n\n### Exit statuses\n\n### Builtins\n\n#### `echo`\n\n#### `cd`\n\n```c\nint\tft_cd(t_fullcmd fullcmd, char ***env)\n{\n\tchar\t*path;\n\tchar\t*oldpath;\n\tchar\t*home;\n\n\tpath = NULL;\n\thome = ft_getenv(\"HOME\", *env);\n\toldpath = ft_strdup(getcwd(NULL, 0));\n\tdetermine_path(\u0026path, home, fullcmd, env);\n\tif (path == NULL)\n\t\treturn (ft_printf(\"cd: HOME not set\\n\"), 1);\n\tif (change_path(path, fullcmd))\n\t\treturn (1);\n\tpath = ft_strdup(getcwd(NULL, 0));\n\tupdate_pwd(path, oldpath, env);\n\treturn (free(path), free(oldpath), free(home), 0);\n}\n```\n\nThe `cd` command needs to take into account the current directory to be able to go back to it if the user wants to go back to the previous directory.\n\nThe `determine_path` function is used to handle the cases not handled by the `chdir` function, such as `cd -` or expanding the `~` token to `/Users/user_directory`.\n\nThe `change_path` function is used to change the current directory to the one specified by the user by calling the `chdir` function. It also handles the errors returned by `chdir`, for example if the directory does not exist.\n\nIt then updates the `PWD` and `OLDPWD` environment variables by calling the `update_pwd` function.\n\n#### `pwd`\n\n```c\nint\tft_pwd(char **env)\n{\n\tchar\t*wd;\n\n\twd = ft_getenv(\"PWD\", env);\n\tif (write(STDOUT_FILENO, wd, ft_strlen(wd)) \u003c 0)\n\t\treturn (ft_putendl_fd(strerror(errno), STDERR_FILENO), -1);\n\tif (write(STDOUT_FILENO, \"\\n\", 1) \u003c 0)\n\t\treturn (ft_putendl_fd(strerror(errno), STDERR_FILENO), -1);\n\treturn (0);\n}\n```\n\n`pwd` simply fetches the value of the `PWD` environment variable and prints it.\n\n#### `export`\n\n#### `unset`\n\n#### `env`\n\n```c\nint\tft_env(t_fullcmd fullcmd, char ***env)\n{\n\tint\ti;\n\n\ti = 0;\n\tif (fullcmd.argums[1] != NULL)\n\t\treturn (ft_putendl_fd(\"The builtin env does not take arguments or options.\", STDERR_FILENO), -1);\n\twhile ((*env)[i])\n\t{\n\t\tprintf(\"%s\\n\", (*env)[i]);\n\t\ti++;\n\t}\n\treturn (0);\n}\n```\n\nThe `env` command simply iterates through the environment variables fetched at the entry of the program and prints them.\n\n#### `exit`\n\n```c\nvoid\tft_exit(t_fullcmd fullcmd, char ***env)\n{\n\tint i;\n\n\t(void)env;\n\ti = 0;\n\tft_putstr_fd(\"exit\\n\", STDOUT_FILENO);\n\tif (fullcmd.argums[1] != NULL \u0026\u0026 fullcmd.argums[2] != NULL)\n\t\treturn (ft_putendl_fd(\"exit: too many arguments\", STDERR_FILENO), (void)0);\n\tif (fullcmd.argums[1] != NULL)\n\t{\n\t\twhile (fullcmd.argums[1][i])\n\t\t{\n\t\t\tif (ft_isdigit(fullcmd.argums[1][i]) == 0)\n\t\t\t{\n\t\t\t\tft_putendl_fd(\"exit: numeric argument required\", STDERR_FILENO);\n\t\t\t\texit(255);\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t\texit(ft_atoi(fullcmd.argums[1]));\n\t}\n\texit(0);\n}\n```\n\nThe `exit` command is used to exit the shell. It takes into account the exit status specified by the user, and if it is not specified, it exits with the 0 exit status.\n\n### Signals\n\n#### `Ctrl-C`\n\n```c\nvoid\tsigint_handler(int sig)\n{\n\t(void)sig;\n\tft_putchar_fd('\\n', 1);\n\trl_replace_line(\"\", 0);\n\trl_on_new_line();\n\trl_redisplay();\n}\n```\n\nHandling the `Ctrl-C` signal is made easier by the use of the `readline` library, allowing us to clear the current line and display a new prompt seamlessly.\n\n#### `Ctrl-D`\n\n`Ctrl-D` actually prints an `EOF` character, we therefore only need to check if the line read by `readline` is empty to exit the shell.\n\n## Bonus part\n\n### Operators\n\nThe `\u0026\u0026` and `||` operators actually need to be handled carefully, as they can be chained together and that every command in the chain depends on the exit status of the previous one.\n\nTo achieve so, the most efficient way is to use an **Abstract Syntax Tree (AST)**.\n\nAbstract Syntax Trees are often used to represent mathematical operations with priorities. For example $4 + 2 * 10 + 3 * (5 + 1)$ would be represented as:\n\n\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"./assets/ast.svg\" /\u003e\n\u003c/p\u003e\n\nIn our case, we will use an AST to represent the commands and operators entered by the user. For example, the command `ls \u0026\u0026 pwd || echo \"Hello World\"` would be represented as:\n\n\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"./assets/ast_shell.svg\" /\u003e\n\u003c/p\u003e\n\n#### Step-by-step\n\n1. The `ls` command is executed.\n2. The `pwd` command is executed if the `ls` command exited with a 0 exit status.\n3. The `echo \"Hello World\"` command is executed if one of the `ls` **or** `pwd` commands exited with a non-zero exit status.\n\n\nBy traversing the AST, we can execute the commands in the right order, and we can also handle the `\u0026\u0026` and `||` operators' particularities properly.\n\n### Wildcard expansion\n\n## Credits\n\n*🙇🏻‍♂️ This project has been done in great collaboration with [ysmeding](https://github.com/ysmeding).*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleogaudin%2Fminishell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleogaudin%2Fminishell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleogaudin%2Fminishell/lists"}