I made my own Unix shell
01 Jul 2023
For an assignment at Codam, I made a Unix shell in C, together with my team mate. It was basically a simplified version of Bash. You can find it on my github.
Some of its features:
- execute multiple commands in a pipeline, using the PATH environmental variable or absolute paths
- redirecting inputs and outputs, including heredoc
- custom signal handling, so a signal like SIGINT only terminates the running process, and not the entire shell
- expanding environment variables like
$USER, and$?to get the exit status of the previous command - a number of builtins like
exit,echo,pwd,cd - single and double quote handling
Cool things I learned
- Lexical analysis, to split the input string into a sequence of tokens, which can be operators or words. Applying the Finite State Machine (FSM) model helps to keep in control of all edge cases (e.g. quote handling and operators consisting of multiple chars).
- Forking processes, and using Pipes to communicate between processes, to create a nice pipeline.
- Signals and how EOF is not one
Surprising things I learned about Bash
- All commands in a pipeline run parallel. Some commands, however, will wait for input from the command to its left. For exmaple:
cat | lswill directly runls, whilecatis still waiting for input from standard-in. - Arguments and redirections don’t need to be in order. For example:
echo hello > outfile worldwill put “hello world” in the outfile. - Variable expansion also happens within double quotes. Variables are expanded outside of quotes or in double quotes, but not in single quotes. For example:
echo "my home folder: $HOME"will print the home folder location whileecho 'my home folder: $HOME'will not. - Variable expansion is very tolerant of other chars glued to it. For example
echo ABC$HOME+-just givesABC/Users/<username>+- - Redirections take precedence over pipes. For example
echo "hello world" > outfile | wc -wwill put “hello world” in the outfile. Since the output is redirected to the file, it will not go into the pipe, so the word count command (wc) will receive no input and will return0. - Pipes and redirection operators don’t need spaces. For example:
echo "hello world"|wc>outfilewill work fine. I was surprised by this lenience, because in if-statements, Bash demands spaces around the square brackets and the==. For example in a case like:if [ 2 == 2 ]; then echo "equal"; fi. - You can have multiple redirections of the same type. For example:
echo "hello world" > outfile > outfile2 > outfile3will create all the outfiles, but the echoed text will only go into the last file. - Redirections don’t need a command. For example:
> file1 > file2 > file3will just create 3 files. Similarly,< file1 < file2 < file3will check if all the files exist and have read permissions. - exit | exit won’t exit bash, because in a pipeline all commands (including builtins) run in a subshell.
- Be careful of accidental history expansion In Bash version 3.2, which I currently use, the following does not work:
echo "hello!". The reason is that the exclamation mark is a special character used for history expansion.