{"id":8750,"date":"2025-07-31T16:45:19","date_gmt":"2025-07-31T16:45:19","guid":{"rendered":"https:\/\/namastedev.com\/blog\/?p=8750"},"modified":"2025-07-31T16:45:19","modified_gmt":"2025-07-31T16:45:19","slug":"write-a-simple-shell-in-c","status":"publish","type":"post","link":"https:\/\/namastedev.com\/blog\/write-a-simple-shell-in-c\/","title":{"rendered":"Write a Simple Shell in C"},"content":{"rendered":"<h1>Building a Simple Shell in C: A Step-by-Step Guide<\/h1>\n<p>If you&#8217;re venturing into systems programming or want to strengthen your understanding of process management, building a simple shell in C is an excellent project. Not only does it deepen your grasp of operating system concepts, but it&#8217;s also a practical way to learn about process execution, input\/output redirection, and more. In this article, we&#8217;ll walk through the steps to create a basic shell from scratch.<\/p>\n<h2>What is a Shell?<\/h2>\n<p>A shell is a command-line interface that allows users to interact with the operating system. It interprets commands, manages processes, and provides an environment for running programs. In UNIX-like systems, the shell is an essential part of user experience, enabling task automation through scripts and command execution.<\/p>\n<h2>Understanding the Basics<\/h2>\n<p>Before we jump into the code, it&#8217;s important to understand some basic concepts:<\/p>\n<ul>\n<li><strong>Processes:<\/strong> A process is an instance of a program that is executed. The shell creates child processes to execute commands.<\/li>\n<li><strong>Forking:<\/strong> The `fork()` system call is used to create a new process by duplicating the existing one. The new process is called a child process.<\/li>\n<li><strong>Executing Programs:<\/strong> The `exec` family of functions replaces the current process image with a new program image.<\/li>\n<li><strong>Waiting for Processes:<\/strong> The `wait()` function makes the parent process wait until a child process terminates.<\/li>\n<\/ul>\n<h2>Setting Up the Environment<\/h2>\n<p>To start building our shell, ensure you have the necessary tools installed. You\u2019ll need:<\/p>\n<ul>\n<li>A C compiler like GCC<\/li>\n<li>A text editor of your choice (Vim, Nano, etc.)<\/li>\n<li>Access to a UNIX-like environment (Linux or macOS preferred)<\/li>\n<\/ul>\n<p>Once your environment is set up, create a new directory for your shell project:<\/p>\n<pre>\nmkdir simple_shell\ncd simple_shell\n<\/pre>\n<h2>The Basic Structure of a Shell<\/h2>\n<p>We will start with a basic structure in C. Create a file named <strong>simple_shell.c<\/strong>:<\/p>\n<pre>\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;string.h&gt;\n\n#define MAX_INPUT_SIZE 1024\n\nint main() {\n    char input[MAX_INPUT_SIZE];\n\n    while (1) {\n        printf(\"simple_shell&gt; \"); \/\/ Display a prompt\n        fgets(input, MAX_INPUT_SIZE, stdin); \/\/ Get user input\n        printf(\"You entered: %s\", input); \/\/ Echo the input\n    }\n    return 0;\n}\n<\/pre>\n<p>Here\u2019s a breakdown of what\u2019s happening:<\/p>\n<ul>\n<li>We include the necessary header files for input\/output, memory management, process control, and string handling.<\/li>\n<li>We define a constant <strong>MAX_INPUT_SIZE<\/strong> to limit user input length.<\/li>\n<li>Inside the main loop, we prompt the user for input, read the input, and then echo it back.<\/li>\n<\/ul>\n<h2>Compiling and Running Your Shell<\/h2>\n<p>To compile your code, execute:<\/p>\n<pre>\ngcc -o simple_shell simple_shell.c\n<\/pre>\n<p>Run your shell with:<\/p>\n<pre>\n.\/simple_shell\n<\/pre>\n<p>At this point, you should see a prompt that waits for user input. When you enter a command, it echoes it back to you.<\/p>\n<h2>Executing Commands<\/h2>\n<p>The next step is to allow the shell to execute user commands. Modify your code to include the ability to fork a process and execute commands using <strong>execvp<\/strong>:<\/p>\n<pre>\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;string.h&gt;\n\n#define MAX_INPUT_SIZE 1024\n#define MAX_ARG_SIZE 100\n\nint main() {\n    char input[MAX_INPUT_SIZE];\n    char *args[MAX_ARG_SIZE];\n\n    while (1) {\n        printf(\"simple_shell&gt; \");\n        fgets(input, MAX_INPUT_SIZE, stdin);\n        input[strcspn(input, \"n\")] = 0; \/\/ Remove newline character\n\n        \/\/ Tokenize input\n        char *token = strtok(input, \" \");\n        int i = 0;\n        while (token != NULL) {\n            args[i++] = token;\n            token = strtok(NULL, \" \");\n        }\n        args[i] = NULL; \/\/ Terminate the array with NULL\n\n        if (fork() == 0) {\n            execvp(args[0], args); \/\/ Execute the command\n            perror(\"execvp failed\"); \/\/ Error handling\n            exit(EXIT_FAILURE);\n        }\n        else {\n            wait(NULL); \/\/ Parent waits for the child process to complete\n        }\n    }\n    return 0;\n}\n<\/pre>\n<p>### Explanation of the Changes Made:<\/p>\n<ul>\n<li>We added an <strong>args<\/strong> array that holds the command and its arguments after tokenization.<\/li>\n<li>We use <strong>strtok<\/strong> to split the input string into tokens based on spaces.<\/li>\n<li>When forking, the child process uses <strong>execvp<\/strong> to execute the command entered by the user.<\/li>\n<li>If the <strong>execvp<\/strong> call fails, an error message is printed.<\/li>\n<li>The parent process calls <strong>wait<\/strong> to ensure it waits for the child to finish before prompting for input again.<\/li>\n<\/ul>\n<h2>Adding Built-in Commands<\/h2>\n<p>It&#8217;s typical for shells to support built-in commands like <strong>exit<\/strong>. Let\u2019s enhance our shell to handle such commands:<\/p>\n<pre>\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;string.h&gt;\n\n#define MAX_INPUT_SIZE 1024\n#define MAX_ARG_SIZE 100\n\nvoid execute_command(char *input) {\n    char *args[MAX_ARG_SIZE];\n    char *token = strtok(input, \" \");\n    int i = 0;\n\n    while (token != NULL) {\n        args[i++] = token;\n        token = strtok(NULL, \" \");\n    }\n    args[i] = NULL;\n\n    if (strcmp(args[0], \"exit\") == 0) {\n        exit(0); \/\/ Exit the shell\n    }\n\n    if (fork() == 0) {\n        execvp(args[0], args);\n        perror(\"execvp failed\");\n        exit(EXIT_FAILURE);\n    } else {\n        wait(NULL);\n    }\n}\n\nint main() {\n    char input[MAX_INPUT_SIZE];\n\n    while (1) {\n        printf(\"simple_shell&gt; \");\n        fgets(input, MAX_INPUT_SIZE, stdin);\n        input[strcspn(input, \"n\")] = 0; \/\/ Remove the newline character\n        execute_command(input); \/\/ Execute the command\n    }\n    return 0;\n}\n<\/pre>\n<p>### Key Changes:<\/p>\n<ul>\n<li>We introduced a function <strong>execute_command<\/strong> to handle command execution.<\/li>\n<li>The built-in <strong>exit<\/strong> command checks if the user wants to terminate the shell, providing a clean way to exit.<\/li>\n<li>Functionality is modularized for better readability and maintainability.<\/li>\n<\/ul>\n<h2>Implementing Input\/Output Redirection<\/h2>\n<p>A well-rounded shell should also handle input and output redirection (using ). We can extend our shell to support these features.\u201d Here\u2019s how:<\/p>\n<pre>\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;unistd.h&gt;\n#include &lt;string.h&gt;\n#include &lt;fcntl.h&gt;\n\n#define MAX_INPUT_SIZE 1024\n#define MAX_ARG_SIZE 100\n\nvoid execute_command(char *input) {\n    char *args[MAX_ARG_SIZE];\n    char *token = strtok(input, \" \");\n    int i = 0;\n    int input_redirect = 0;\n    int output_redirect = 0;\n    char *input_file = NULL;\n    char *output_file = NULL;\n\n    while (token != NULL) {\n        if (strcmp(token, \"&lt;\") == 0) {\n            input_redirect = 1;\n            token = strtok(NULL, \" \");\n            input_file = token;\n        } else if (strcmp(token, \"&gt;\") == 0) {\n            output_redirect = 1;\n            token = strtok(NULL, \" \");\n            output_file = token;\n        } else {\n            args[i++] = token;\n        }\n        token = strtok(NULL, \" \");\n    }\n    args[i] = NULL;\n\n    if (strcmp(args[0], \"exit\") == 0) {\n        exit(0);\n    }\n\n    if (fork() == 0) {\n        \/\/ Handle output redirection\n        if (output_redirect) {\n            int fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);\n            dup2(fd, 1); \/\/ Redirect stdout to file\n            close(fd);\n        }\n\n        \/\/ Handle input redirection\n        if (input_redirect) {\n            int fd = open(input_file, O_RDONLY);\n            dup2(fd, 0); \/\/ Redirect stdin to file\n            close(fd);\n        }\n\n        execvp(args[0], args);\n        perror(\"execvp failed\");\n        exit(EXIT_FAILURE);\n    } else {\n        wait(NULL);\n    }\n} \n\nint main() {\n    char input[MAX_INPUT_SIZE];\n\n    while (1) {\n        printf(\"simple_shell&gt; \");\n        fgets(input, MAX_INPUT_SIZE, stdin);\n        input[strcspn(input, \"n\")] = 0;\n        execute_command(input);\n    }\n    return 0;\n}\n<\/pre>\n<p>### Enhancements for Redirection:<\/p>\n<ul>\n<li>We integrated checks for <strong>input<\/strong> and <strong>output<\/strong> redirection using .<\/li>\n<li>By opening the specified file and redirecting the relevant file descriptor (stdin or stdout), we allow our shell to work with files seamlessly.<\/li>\n<li>This change allows commands like <strong>cat &lt; input.txt<\/strong> or <strong>echo Hello &gt; output.txt<\/strong> to function in our shell.<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>Congratulations! You&#8217;ve built a simple, yet fully functional command-line shell in C. This project not only allows you to grasp concepts such as process management, command execution, and redirection, but it also sets the foundation for exploring more complex shell functionalities.<\/p>\n<p>As you become more comfortable, consider adding features like handling pipes, supporting environment variables, or even building your own scripting language. The possibilities are endless, and each feature will deepen your understanding of system programming.<\/p>\n<h2>Resources for Further Learning<\/h2>\n<ul>\n<li><a href=\"http:\/\/man7.org\/linux\/man-pages\/man2\/fork.2.html\" target=\"_blank\">Linux Manual Page: fork()<\/a><\/li>\n<li><a href=\"http:\/\/man7.org\/linux\/man-pages\/man3\/exec.3.html\" target=\"_blank\">Linux Manual Page: exec()<\/a><\/li>\n<li><a href=\"https:\/\/www.gnu.org\/software\/libc\/manual\/html_node\/Exec-Function.html\" target=\"_blank\">GNU C Library: exec Functions<\/a><\/li>\n<\/ul>\n<p>By taking the time to build your shell, you&#8217;re engaging with fundamental computing concepts that will serve you well as you advance in your software development career.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Building a Simple Shell in C: A Step-by-Step Guide If you&#8217;re venturing into systems programming or want to strengthen your understanding of process management, building a simple shell in C is an excellent project. Not only does it deepen your grasp of operating system concepts, but it&#8217;s also a practical way to learn about process<\/p>\n","protected":false},"author":133,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1153],"tags":[1232,1231,1233],"class_list":["post-8750","post","type-post","status-publish","format-standard","category-capstone-projects-further-reading","tag-c","tag-shell","tag-unix"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts\/8750","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/users\/133"}],"replies":[{"embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/comments?post=8750"}],"version-history":[{"count":1,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts\/8750\/revisions"}],"predecessor-version":[{"id":8780,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/posts\/8750\/revisions\/8780"}],"wp:attachment":[{"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/media?parent=8750"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/categories?post=8750"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/namastedev.com\/blog\/wp-json\/wp\/v2\/tags?post=8750"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}