{"id":19590650,"url":"https://github.com/aaron-22766/42_minishell","last_synced_at":"2026-05-06T00:03:08.939Z","repository":{"id":166457595,"uuid":"641950339","full_name":"aaron-22766/42_minishell","owner":"aaron-22766","description":"Create a simple shell program, inspired by bash (team project)","archived":false,"fork":false,"pushed_at":"2024-03-01T10:31:21.000Z","size":129,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-26T13:46:53.613Z","etag":null,"topics":["42","42-minishell","42born2code","42cursus","42projects","42school","bash","c","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/aaron-22766.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,"publiccode":null,"codemeta":null}},"created_at":"2023-05-17T13:57:03.000Z","updated_at":"2024-05-20T17:59:15.000Z","dependencies_parsed_at":"2024-02-22T13:25:06.393Z","dependency_job_id":"fe780e4a-41dd-4437-b0f7-3c97591ded29","html_url":"https://github.com/aaron-22766/42_minishell","commit_stats":null,"previous_names":["aaron-22766/3.1_minishell","aaron-22766/42_minishell"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/aaron-22766/42_minishell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-22766%2F42_minishell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-22766%2F42_minishell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-22766%2F42_minishell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-22766%2F42_minishell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aaron-22766","download_url":"https://codeload.github.com/aaron-22766/42_minishell/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-22766%2F42_minishell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32672685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","42-minishell","42born2code","42cursus","42projects","42school","bash","c","shell"],"created_at":"2024-11-11T08:25:49.725Z","updated_at":"2026-05-06T00:03:08.922Z","avatar_url":"https://github.com/aaron-22766.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n\t\u003cp\u003e\n\tminishell\n\t\u003c/p\u003e\n\t\u003cimg src=\"https://github.com/aaron-22766/aaron-22766/blob/main/42-badges/minishelle.png\"\u003e\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003cb\u003e\u003ci\u003eAs beautiful as a shell\u003c/i\u003e\u003c/b\u003e\u003cbr\u003e\u003cbr\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003cimg alt=\"GitHub code size in bytes\" src=\"https://img.shields.io/github/languages/code-size/aaron-22766/42_minishell?color=lightblue\" /\u003e\n\t\u003cimg alt=\"Code language count\" src=\"https://img.shields.io/github/languages/count/aaron-22766/42_minishell?color=yellow\" /\u003e\n\t\u003cimg alt=\"GitHub top language\" src=\"https://img.shields.io/github/languages/top/aaron-22766/42_minishell?color=blue\" /\u003e\n\t\u003cimg alt=\"GitHub last commit\" src=\"https://img.shields.io/github/last-commit/aaron-22766/42_minishell?color=green\" /\u003e\n\u003c/p\u003e\n\n---\n\n## 🗣 About\n\nThis project is about creating a simple shell. Yes, your own little bash. You will learn a lot about processes and file descriptors.\n\nThis is the first team project of the 42 Cursus and I teamed up with [Raffael](https://github.com/Raffael-Passion).\n\n## 🛠 Usage\n\n```shell\n$\u003e make\n$\u003e ./minishell [-v optional]\n▶ $\u003e # you are now in the minishell prompt\n```\n\n## 💬 Description\n\nHere's a rough description about what our minishell does.\n\n### Init shell\n\n* allocate and copy the `extern char **environ` because we need to alter it\n* OLDPWD is added if it doesn't exist\n* PWD is set to the current directory\n* SHLVL gets incremented\n* SHELL gets set to the path of our minishell executable\n\n### Environment\n\n* as we are not allowed to use the functions, we wrote our own implementation for `putenv`, `setenv` and `unsetenv`\n\n### Terminal\n\n* for ignoring the `^C` when ctrl-C is pressed, we remove `ECHOCTL` from the terminal attributes by using the functions `tcgetattr` and `tcsetattr`\n\n### following is the main loop for readline\n\n### Prompt\n\n* prompt is generated from the `PS1` environment variable, if it is set\n* it follows the bash rules for expansion\n* `[`, `]`, `a`, `e`, `n`, `r`, `\\`, `s`, `w`, `W`, `u`, `h`, `H` are supported escape characters that are replaced if preceeded by a backslash\n* user, hostname and working directory are the most useful and require the corresponding environment variables to be set\n* the prompt also starts with a colored `▶` as a status indicator, it's grey by default, green on sucess and red if the execution failed\n\n### Readline\n\n* we call `line = readline(prompt);`\n* if the line is NULL and the readline variable `rl_eor_found` is true, it means that ctrl-D was pressed and the shell should exit (after freeing everything of course)\n* then we add the line to the history if it isn't empty\n* we check the line for only whitespace characters, in which case we don't need to do anything (loop starts over) and the prompt status should turn grey\n\n### Lexer\n\n* the line gets passed to the lexer, which splits it up into a linked list of tokens\n```c\ntypedef struct s_tokens\n{\n    char id;\n    char *content;\n    struct s_tokens *next;\n}   t_tokens;\n```\n* the id specifies what the `content` string actually is, that makes it easy for the parser to loop over it without needing to do strcmp all the time\n* it's also designed as a bitmap to have the two categories `WORD` and `OPERATOR`\n```c\n0b00000001 WORD\n0b00000101 IN_FILE (the word after a IN_RED)\n0b00001001 OUT_FILE (the word after a OUT_RED)\n0b00010001 HEREDOC_EOF (the word after a HEREDOC)\n0b00100001 OUT_A_FILE (the word after a OUT_A_RED)\n0b01000001 COMMAND (the first word between PIPEs that is not one of the above)\n0b00000010 OPERATOR\n0b00000110 IN_RED (\u003c)\n0b00001010 OUT_RED (\u003e)\n0b00010010 HEREDOC (\u003c\u003c)\n0b00100010 OUT_A_RED (\u003e\u003e)\n0b01000010 PIPE (|)\n```\n* the lexer also needs to detect single and double quotes correctly to form the words and not to interpret meta-characters in-between them\n* if there are unclosed quotes, minishell doesn't do the same as bash and will just print a syntax error\n\n### Expander\n\n* the expander is integrated in the parser but the expansion happens before the main parser job anyway\n* all the tokens are analyzed and `WORD`s that are not `HEREDOC_EOF` are expanded\n* `~` is expanded to `HOME`\n* `~+` is expanded to `PWD`\n* `~-` is expanded to `OLDPWD`\n* these tilde expansions are also applied to assignments if the tilde is preceeded immediately by the `=`\n* the following expansions are also used for the heredoc\n* `$` must be followed by a valid variable name - first digit must be alpha or underscore, following alnum or a number of allowed characters\n* also quotes and backslashes need to be handled correctly - var name is only until the first quote unless it's escaped by backlash (simplified example)\n* if a valid name is found, we look if there is a value (`getenv`) in the environment and it gets replaced if there is one\n* escaping backslashes are removed (`\\\\` is just `\\`)\n* all the time quotes are really important to know what gets expanded - nothing between single quotes\n\n### Parser\n\n* the parser receives the tokens and sets up a linked list of commands\n```c\ntypedef struct s_cmds\n{\n    char *path;\n    char **argv;\n    t_tokens *io_red;\n    int fd_in;\n    int fd_out;\n    char builtin;\n    struct s_cmds *next;\n}   t_cmds;\n```\n* after the expansion of the tokens, the quotes are removed from the words where applicable\n* `path` is created later\n* `argv` is a string array containing the arguments that need to be passed to `execve`\n* `io_red` contains only the redirection tokens in order for the executor to set up the redirections\n* `fd_in` and `fd_out` are file descriptors that are set later (default to stdin and stdout) for the executor to set up the redirections\n* `builtin` is set later (default `B_NO`) for the executor to know if which builtin it is\n\n### Executor\n\n#### Builtin\n\n* we check if `argv[0]` is one of our implemented builtins, for this we made a bitmap\n```c\n0b00000000 B_NO\n0b00000001 B_PARENT\n0b00000011 B_EXIT\n0b00000101 B_CD\n0b00001001 B_EXPORT\n0b00010001 B_UNSET\n0b00000010 B_PWD\n0b00000100 B_ECHO\n0b00001000 B_ENV\n```\n* this makes it easy to check where the builtin needs to run (builtin \u0026 `B_PARENT` → should alter the current/parent process, no child needed)\n* `EXPORT`, `EXIT`, `CD` and `UNSET` only work if it's a single command, that's why we check for this in the beginning and run them if that's the case\n\n#### Redirections\n\n* loops over `io_red` tokens and sets `fd_in` and `fd_out` accordingly\n* `OUT_FILE` and `OUT_A_FILE` open/create a file in O_WRONLY with the file name in the `content` of the token and the right permissions\n* `IN_FILE` opens the file in O_RDONLY\n* `HEREDOC` creates a pipe for an \"invisible\" temporary file, has another readline loop where it reads and expands the line and writes it to the pipe - `fd_in` is set to the pipes read end\n\n#### Pipeline\n\n* this is a recursive function (split up into multiple for norminette) that creates all the child processes with the correct pipes\n* if the command is a parent builtin, it just gets ignored\n* the child process then used `dup` to redirect the input and output to/from file or pipe\n* if it's a child builtin, it is executed\n* now we find the absolute path to the executable using the `PATH` variable\n* we call `execve` with `path`, `argv` and `environ`\n* the recursiveness of this function creates all the child processes simultaneously (previous consequtive method is another branch)\n* in the meantime the parent process waits for all childs to exit and afterwards all commands are freed\n\n### Signals\n\n* during the entire parent process ctrl-\\ is ignored\n* ctrl-C will always set a global variable to true, which quits the current processing and returns to readline\n* during readline ctrl-C needs some more functions so that we get a new line because readline doesn't return\n* the heredoc also has a special handler for readline\n* ctrl-\\ isn't handled but it should inside heredocs which is an oversight on our part\n\n### Builtins\n\n#### exit\n\n* exits the shell with the status in the argument or the current status if none is specified\n* also needs a numeric argument for the status otherwise it will error\n\n#### cd\n\n* changes the current directory to the first argument provided\n* can be relative or absolute path\n* `--` changes to `HOME`\n* `-` changes to `OLDPWD`\n* `PWD` and `OLDPWD` are set accordingly\n\n#### export\n\n* with an argument it needs a valid identifier followed by an optional `=` and value\n* creates or changes the value of an existing environment variable\n* if no argument is provided it will print the environment variables in a weird format\n\n#### unset\n\n* with a valid identifier as argument it unsets/deletes the environment variable\n* otherwise it shows an error\n\n#### pwd\n\n* prints the current working directory to the stdout\n\n#### echo\n\n* writes all the arguments to stdout followed by a newline\n* if the option `-n` is specified, no newline is added afterwards\n* some interesting differences are between \"echo\" in just lowercase and any other case version, as the latter would also accept multiple `-n` with as many \"n\" as we want\n\n#### env\n\n* prints the current environment variables to the stdout\n\n\n  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaron-22766%2F42_minishell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faaron-22766%2F42_minishell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaron-22766%2F42_minishell/lists"}