Part 3: Executing Commands in a Custom Shell (C Programming)
In this final part of my custom shell project, we’ll explore how to execute user commands after they’ve been parsed. At this point, the user input has been successfully split into arguments — now it’s time to take action! 💥
Understanding the execute_command Function
The core function here is:
void execute_command(char **args, int argc, Command *command, int command_count)
✅ Matches user input with a predefined command
✅ Executes the corresponding function pointer
✅ Prints an error message if no command matches
Let’s break it down 👇
for (int i = 0; i < command_count; i++) { if (strcmp(args[0], command[i].name) == 0) { command[i].action(args, argc); return; } } printf("Unknown commnad: %s\n", args[0]);
- Command Matching: The shell loops through an array of
Command
structs and compares the input with registered command names. - Function Pointers: If a match is found, the corresponding function is called via a pointer.
- Error Feedback: If no match is found, the shell notifies the user that the command is unknown (with a small typo on purpose? 😉).
The Command Structure
Each command is a struct that looks like this:
typedef struct Command { char *name; void (*action)(char **args, int argc); } Command;
-
name
: the actual command (like"say"
or"date"
) -
action
: a pointer to the function that gets executed
This structure is super handy for adding new commands without writing extra if-else
statements.
A Look at Some Commands
Let’s check out a few implemented commands from the execute.c
file:
🗨️ say_action
void say_action(char **args, int argc) { if (argc > 1) { for (int i = 1; i < argc; i++) { printf("%s ", args[i]); } } printf("\n"); }
Simply echoes back what the user types — a nice test command.
🗓️ date_command
Gives you the current date and time — optionally broken down into year, month, day, hour, minute, or second depending on flags like -dy
, -dm
, etc.
📂 clone_file_command
Copies a file from one location to another using binary read/write.
✂️ cut_file_command
Cuts a file (moves it) to a new location and removes the original — uses file streams and path manipulation.
📁 list_of_directory
Recursively lists directory contents in a tree-like format. Really cool!
Function Pointer Magic 🧙
Using function pointers instead of conditionals makes the design much more scalable. You can add a new command like this:
{"new_command", new_command_action}
…and implement it as a regular function. Done!
Memory Considerations
Each command function takes char **args
and int argc
— just like main(int argc, char **argv)
— keeping things familiar and flexible.
Also, as shown in the main loop:
free_args(args, argc); free(command);
Memory is properly cleaned up after every command execution ✅
Why This Approach?
📌 Modular Design: Each command is self-contained and easy to test or extend.
📌 Dynamic Execution: Commands can be dispatched efficiently using function pointers.
📌 User-Friendly Feedback: Invalid inputs are handled gracefully.
📌 Scalable Structure: Adding a new command takes just 2 lines in the main file!
Wrap-Up: Building a Custom Shell 🧠
Over the past three parts, we built a functional, modular shell in C:
- ✅ Part 1: Read user input dynamically
- ✅ Part 2: Parse input into arguments
- ✅ Part 3 (this one): Execute commands based on user input
Next step? Add more features, maybe some pipes, or even background execution!
📂 GitHub: https://github.com/Ertugrulmutlu/shell_of_mine
Did you find this series helpful? What feature should I add next? Let me know! 👇
Top comments (0)