This project is our (@antonweizmann) own little shell written completely in C. While it is not as feature complete as bash or zsh, it provides most features typically used.
- clone repository
- open folder in terminal
- run "make init_submodules"
- run "make"
- run "./minishell" A minishell prompt should appear, you can now use the terminal as usual, to return back to your original shell press ctrl-D or run "exit".
- executing commands
- command history
- pipes: "|"
- operators with priority: "&&", "||", "(", ")"
- redirections: "<", "<<", ">", ">>"
- environment variables: "$"
- quotation marks
- signals: ctrl-C, ctrl-D, ctrl-\
- built-ins: cd, pwd, echo, env, export, unset, exit
- Input: We read input using
readline
to display the prompt and get the input unlessstdin
is not a tty, in which case we useget_next_line
from libft. - Tokenization: The input string gets tokenized into a doubly linked list made up of these tokens:
T_WORD
T_SEPARATOR
T_PIPE
T_AND
T_OR
T_LPAREN
T_RPAREN
T_REDIR_IN
T_REDIR_OUT
T_REDIR_APPEND
T_REDIR_HEREDOC
- Recursive descent: The token list is recursively converted into a binary tree by splitting it into smaller parts and removing already used tokens.
- The tree consists of the following nodes, each with a
type
variable to identify it:- Basic Binary Node: With two pointers to the next nodes, used for
AND
,OR
, andPIPE
. - Redirect Node: With one pointer to a new subtree, used for storing redirect information outside of brackets and starting a new subshell. Example:
cd .. && pwd
vs(cd ..) && pwd
. - Command Node: With a double character array for the command arguments and another array for redirects specific to this command.
- Basic Binary Node: With two pointers to the next nodes, used for
- Post-Parsing Resolution: After the tree is parsed, all heredoc redirects are resolved. This ensures that user input for heredoc is not prematurely read, particularly in cases where the overall command syntax is incorrect.
Once the input has been parsed into a binary tree structure, the shell enters the execution phase. This phase involves traversing the binary tree, handling each type of node appropriately to execute commands, handle logical operators, manage pipelines, and handle redirections. The following sections outline the steps involved in executing the parsed command tree.
- Description: Command nodes contain the basic commands and their arguments.
- Execution Steps:
- Forking: The shell forks a new process using
fork()
. - Redirections: Before executing the command, the shell sets up input/output redirections based on the redirections list associated with the node.
- Executing: The child process uses
execve()
to execute the command with the provided arguments. - Waiting: The parent process waits for the child process to complete using
waitpid()
.
- Forking: The shell forks a new process using
- Description: These nodes handle logical operators (
&&
,||
) and pipelines (|
). - Execution Steps:
- AND (
&&
):- Execute the left subtree.
- If the left subtree succeeds (returns 0), execute the right subtree.
- OR (
||
):- Execute the left subtree.
- If the left subtree fails (returns non-zero), execute the right subtree.
- PIPE (
|
):- Create a pipe using
pipe()
. - Fork two processes:
- The first process redirects its stdout to the write end of the pipe and executes the left subtree.
- The second process redirects its stdin to the read end of the pipe and executes the right subtree.
- Close the pipe and wait for both processes to complete.
- Create a pipe using
- AND (
- Description: These nodes handle input (
<
), output (>
), append (>>
), and heredoc (<<
) redirections. - Execution Steps:
- Input Redirection (
<
):- Open the input file and redirect stdin to this file.
- Output Redirection (
>
):- Open/Create the output file and redirect stdout to this file.
- Append Redirection (
>>
):- Open/Create the output file in append mode and redirect stdout to this file.
- Heredoc Redirection (
<<
):- Read input until a specified delimiter is encountered and use this input as stdin for the command.
- Input Redirection (
The shell must handle various errors , including:
- Command not found
- Permission denied
- File not found for redirections
- Syntax errors in the command tree
- Traverse the Tree: The shell begins by traversing the root of the binary tree.
- Execute Nodes: Based on the type of node encountered, the appropriate execution function is called.
- Handle Operators and Redirections: Logical operators and redirections are handled according to their semantics.
- Monitor Processes: The shell monitors the execution of commands and handles process termination and exit statuses. ficiently.
The tester is a modivied version of this one, so props to @zstenger93. We added some tests, commented some out that are not in the scope of the subject and rewrote the valgrind test so it also checks for "still reachable" and added 'valgrand bonus' and 'valgrind all' tests.
For more infos have a look at the README.md
in the mstest
folder.
You can either copy the whole mstest folder into your project or change the MINISHELL_PATH
at the top of tester.sh
.
To execute go into the project folder and type bash ./mstest/tester.sh
.