{"id":20137453,"url":"https://github.com/steliospapamichail/unixshell","last_synced_at":"2026-04-07T20:31:24.022Z","repository":{"id":154295381,"uuid":"425551684","full_name":"SteliosPapamichail/UnixShell","owner":"SteliosPapamichail","description":"A custom UNIX shell that supports normal and multiple command inputs, simple I/O redirection, and multi-pipped commands.","archived":false,"fork":false,"pushed_at":"2021-12-09T17:38:45.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-29T14:50:32.267Z","etag":null,"topics":["bash","c","linux","linux-shell","shell","unix"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SteliosPapamichail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2021-11-07T16:11:49.000Z","updated_at":"2022-02-11T12:03:30.000Z","dependencies_parsed_at":null,"dependency_job_id":"394da46c-1879-4020-bf40-0ae3a4b1671f","html_url":"https://github.com/SteliosPapamichail/UnixShell","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SteliosPapamichail/UnixShell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteliosPapamichail%2FUnixShell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteliosPapamichail%2FUnixShell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteliosPapamichail%2FUnixShell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteliosPapamichail%2FUnixShell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SteliosPapamichail","download_url":"https://codeload.github.com/SteliosPapamichail/UnixShell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SteliosPapamichail%2FUnixShell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31528269,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T16:28:08.000Z","status":"ssl_error","status_checked_at":"2026-04-07T16:28:06.951Z","response_time":105,"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":["bash","c","linux","linux-shell","shell","unix"],"created_at":"2024-11-13T21:27:46.701Z","updated_at":"2026-04-07T20:31:23.987Z","avatar_url":"https://github.com/SteliosPapamichail.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Unix shell built by Stelios Papamichail as part of an assignment for the course of CS-345 Operating Systems at the University Of Crete.\n\n## Command parsing logic:\n\nWhenever a line is entered in the shell, the `parseInput()` function is called. This function will check the type of command(s) entered\n(i.e. single command, multiple commands, pipes, redirection) and will then call the appropriate\nfunctions for their execution. In every case, the `parseCommandWords()` function is called\nwhich simply breaks down the given command into words/args. In cases where we have multiple\ncommands (i.e. pipes, multi-command input,etc.), the input is first broken down into commands\nand then `parseCommandWords()` is called.\n\n## Single command execution:\n\nWhen a single command is given as input to the shell, I first check if it's one of the\ncommands that can be executed without the need for system calls via the `handleCustomCommands()`\nfunction. These commands include the cd and exit commands. The cd is executed using `chdir()`\nwhile exit is executed using... well `exit()`.\n\n## Multi-command execution:\n\nOnce multiple commands have been identified (through ';'), the initial input is first\nbroken down into the individual commands, and then each command is broken down into its\ncommand words. Once all of the above are ready, `execSystemCommand()` is called for each\none of those commands. This function uses `fork()` to create a child process that will execute the given command while the\nparent will `wait()` for it to finish.\n\n## Piped commands:\n\nIn this case, the input is once again split into commands and then into command words just\nlike above. Once finished, `execPipedCommands()` is called which first counts the number of\npipes given, and then creates the appropriate pipes using `pipe()` in order to get the required\nfile descriptors for each one so that interprocess communication is established. Once finished,\n`fork()` is called for each command and then depending on whether or not the current command\nis the first or last one, `dup2()` is used to copy the parent's file descriptors and redirect I/O,\nthen the inherited file descriptors are closed and the command is executed using `execvp()`.\nWhen the parent regains control again, it closes its own file descriptors and calls `wait()` for\neach child process.\n\n## Redirection:\n\nWhen a redirection char is identified (`\u003c,\u003e or \u003e\u003e`), the parseInput() function first retrieves\nthe entered delimeter and then splits the input based on that delimeter using `splitAtRedirectionDelim()`.\nThis function uses `strtok()` to split the given input based on the delimeter since `strsep() `\naccepts only char delimeters but in this case, we could have a string (\"\u003e\u003e\") delimeter.\nOnce the two parts of the redirection command have been split, `execRedirectionCommands()` is \ncalled. This function checks the value of the delimeter first, if it contains the `\u003e`, then\nwe know that the command to the left of the `\u003e` will need to redirect its output to the input\nof the command on the right of the `\u003e` delimeter. So, it splits the left hand-side command into\nits parts/words, then depending on whether the delimeter equals `\u003e` or `\u003e\u003e`, `open()` is used\nto get the required file descriptor. In the first case, since we want to overwrite the previous\ncontents of the file we are writing to, we add the extra `O_TRUNC` flag. In the latter case,\nwe use the `O_APPEND` in order to append to it. Once the file descriptor is created, `fork()` is\nused to create the child process that will execute the command using `execvp()`. Before executing,\nI use `dup2()` to redirect the output to `STD_IN` and then close the initial file descriptor.\n\nThe logic is similar for the `\u003c` case, but in that case, the file on the right of the delimeter\nwill be passed as input to the command on the left. This means that the function opens the\nfile on the right for reading, uses `dup2()` to redirect the output, closes the initial file descriptor\nand then executes the command.\n\n## Final thoughts, observations \u0026 potential improvements:\n\nThe code could definitely use some cleaning up (didn't have spare time) and it definitely could\nbe split up into smaller .c source files to improve clarity and maintainability. Support for\nmultiple redirection commands could also be added, as well as a command history using a simple buffer.\n\nI would also like to point out, that running the executable using `valgrind`, upon exiting you\ncan see that the only open file descriptors are the three standard ones (input,output and error)\nwhich means that all fds are properly closed. There are also no memory leaks (to my knowledge after testing)\napart from the argv array that is passed to all `execvp()` commands since when it's successful,\nthe process is replaced and there is no point in freeing that memory before hand (from why I read).\nIf it fails, I've made sure to free that memory :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteliospapamichail%2Funixshell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteliospapamichail%2Funixshell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteliospapamichail%2Funixshell/lists"}