In this repository, you will find a project consisting of the closest possible recreation of a simple UNIX command interpreter in C programming language, that is, a shell of its own.
Basically, a shell is a command line interpreter, which contains an interface that allows users to execute commands and perform different actions such as file manipulation on UNIX-based operating systems.
Img source: https://opensource.com/article/19/9/getting-started-zsh
Each of the files you can see plays an important role for the project, because when all of them are finally compressed (compiled) and form an executable file, it will contain the correct shell operation. Here is a brief description of each one:
File | Description |
---|---|
_built-in.c | Find the built-in or non-built-in command |
_env.c | Prints a list of environment variables |
_execute.c | Execute non-built-ins commands |
_exit.c | Exits the program |
_findpath.c | Find the PATH in the environment variables |
_freestrs.c | Frees the memory occupied by any string |
_notfound.c | Displays an error when the user input is not a valid command |
_printnumber.c | Prints an integer |
_strcat.c | Concatenates two strings |
_strcmp.c | Compares two strings |
_strcpy.c | Copies a string |
_strlen.c | Returns the length of a string |
_strtok.c | Tokenize strings |
shell.c | Main shell functionality |
shell.h | All prototypes of the functions used |
Let us consider a flowchart that will help us to understand the operation of the program in a logical way:
You can compile the program with gcc and different flags that will check for correct operation, which check for errors, warnings and so on. Here is the complete command:
gcc -Wall -Werror -Wextra -pedantic -std=gnu89 *.c -o shell
shell will be the name of the file that will be created as executable.
In addition, after compiling the program, you can run it with the valgrind tool, which helps in debugging memory and performance problems of a program. For this, we will provide you with the command ready with the respective flags, these serve a similar function to those provided for the previous compilation, to help verify the correct operation of the program in general, as well as to make it portable for different devices. Take it below:
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --vgdb=no ./shell
This shell can be executed in two different ways:
Running the executable file and accessing it directly:
Without directly accessing the executable file:
Write a simple UNIX command interpreter.
Read or watch:
- Unix shell
- Thompson shell
- Ken Thompson
- Everything you need to know to start coding your own shell concept page
man or help:
sh
(Runsh
as well)
At the end of this project, you are expected to be able to explain to anyone, without the help of Google:
- Who designed and implemented the original Unix operating system
- Who wrote the first version of the UNIX shell
- Who invented the B programming language (the direct predecessor to the C programming language)
- Who is Ken Thompson
- How does a shell work
- What is a pid and a ppid
- How to manipulate the environment of the current process
- What is the difference between a function and a system call
- How to create processes
- What are the three prototypes of
main
- How does the shell use the
PATH
to find the programs - How to execute another program with the
execve
system call - How to suspend the execution of a process until one of its children terminates
- What is
EOF
/ “end-of-file”?
- Allowed editors:
vi
,vim
,emacs
- All your files will be compiled on Ubuntu 20.04 LTS using
gcc
, using the options-Wall -Werror -Wextra -pedantic -std=gnu89
- All your files should end with a new line
- A
README.md
file, at the root of the folder of the project is mandatory - Your code should use the
Betty
style. It will be checked using betty-style.pl and betty-doc.pl - Your shell should not have any memory leaks
- No more than 5 functions per file
- All your header files should be include guarded
- Use system calls only when you need to (why?)
- Unless specified otherwise, your program must have the exact same output as
sh
(/bin/sh
) as well as the exact same error output. - The only difference is when you print an error, the name of the program must be equivalent to your
argv[0]
(See below)
Example of error with sh
:
$ echo "qwerty" | /bin/sh
/bin/sh: 1: qwerty: not found
$ echo "qwerty" | /bin/../bin/sh
/bin/../bin/sh: 1: qwerty: not found
$
Same error with your program hsh
:
$ echo "qwerty" | ./hsh
./hsh: 1: qwerty: not found
$ echo "qwerty" | ./././hsh
./././hsh: 1: qwerty: not found
$
access
(man 2 access)chdir
(man 2 chdir)close
(man 2 close)closedir
(man 3 closedir)execve
(man 2 execve)exit
(man 3 exit)_exit
(man 2 _exit)fflush
(man 3 fflush)fork
(man 2 fork)free
(man 3 free)getcwd
(man 3 getcwd)getline
(man 3 getline)getpid
(man 2 getpid)isatty
(man 3 isatty)kill
(man 2 kill)malloc
(man 3 malloc)open
(man 2 open)opendir
(man 3 opendir)perror
(man 3 perror)read
(man 2 read)readdir
(man 3 readdir)signal
(man 2 signal)stat
(__xstat) (man 2 stat)lstat
(__lxstat) (man 2 lstat)fstat
(__fxstat) (man 2 fstat)strtok
(man 3 strtok)wait
(man 2 wait)waitpid
(man 2 waitpid)wait3
(man 2 wait3)wait4
(man 2 wait4)write
(man 2 write)
Your shell will be compiled this way:
gcc -Wall -Werror -Wextra -pedantic -std=gnu89 *.c -o hsh
Your shell should work like this in interactive mode:
$ ./hsh
($) /bin/ls
hsh main.c shell.c
($)
($) exit
$
But also in non-interactive mode:
$ echo "/bin/ls" | ./hsh
hsh main.c shell.c test_ls_2
$
$ cat test_ls_2
/bin/ls
/bin/ls
$
$ cat test_ls_2 | ./hsh
hsh main.c shell.c test_ls_2
hsh main.c shell.c test_ls_2
$
- Write a
README
- Write a
man
for your shell. - You should have an
AUTHORS
file at the root of your repository, listing all individuals having contributed content to the repository. Format, see Docker
README.md, man_1_simple_shell, AUTHORS
Write a beautiful code that passes the Betty checks
Write a UNIX command line interpreter.
- Usage:
simple_shell
Your Shell should:
- Display a prompt and wait for the user to type a command. A command line always ends with a new line.
- The prompt is displayed again each time a command has been executed.
- The command lines are simple, no semicolons, no pipes, no redirections or any other advanced features.
- The command lines are made only of one word. No arguments will be passed to programs.
- If an executable cannot be found, print an error message and display the prompt again.
- Handle errors.
- You have to handle the “end of file” condition (
Ctrl+D
)
You don’t have to:
- use the
PATH
- implement built-ins
- handle special characters :
"
,'
,`
,\
,*
,&
,#
- be able to move the cursor
- handle commands with arguments
julien@ubuntu:~/shell$ ./shell
#cisfun$ ls
./shell: No such file or directory
#cisfun$ /bin/ls
barbie_j env-main.c exec.c fork.c pid.c ppid.c prompt prompt.c shell.c stat.c wait
env-environ.c exec fork mypid ppid printenv promptc shell stat test_scripting.sh wait.c
#cisfun$ /bin/ls -l
./shell: No such file or directory
#cisfun$ ^[[D^[[D^[[D
./shell: No such file or directory
#cisfun$ ^[[C^[[C^[[C^[[C
./shell: No such file or directory
#cisfun$ exit
./shell: No such file or directory
#cisfun$ ^C
julien@ubuntu:~/shell$ echo "/bin/ls" | ./shell
#cisfun$ barbie_j env-main.c exec.c fork.c pid.c ppid.c prompt prompt.c shell.c stat.c wait
env-environ.c exec fork mypid ppid printenv promptc shell stat test_scripting.sh wait.c
#cisfun$ julien@ubuntu:~/shell$
Simple shell 0.1 +
- Handle command lines with arguments
Simple shell 0.2 +
- Handle the
PATH
fork
must not be called if the command doesn’t exist
julien@ubuntu:~/shell$ ./shell_0.3
:) /bin/ls
barbie_j env-main.c exec.c fork.c pid.c ppid.c prompt prompt.c shell_0.3 stat test_scripting.sh wait.c
env-environ.c exec fork mypid ppid printenv promptc shell shell.c stat.c wait
:) ls
barbie_j env-main.c exec.c fork.c pid.c ppid.c prompt prompt.c shell_0.3 stat test_scripting.sh wait.c
env-environ.c exec fork mypid ppid printenv promptc shell shell.c stat.c wait
:) ls -l /tmp
total 20
-rw------- 1 julien julien 0 Dec 5 12:09 config-err-aAMZrR
drwx------ 3 root root 4096 Dec 5 12:09 systemd-private-062a0eca7f2a44349733e78cb4abdff4-colord.service-V7DUzr
drwx------ 3 root root 4096 Dec 5 12:09 systemd-private-062a0eca7f2a44349733e78cb4abdff4-rtkit-daemon.service-ANGvoV
drwx------ 3 root root 4096 Dec 5 12:07 systemd-private-062a0eca7f2a44349733e78cb4abdff4-systemd-timesyncd.service-CdXUtH
-rw-rw-r-- 1 julien julien 0 Dec 5 12:09 unity_support_test.0
:) ^C
julien@ubuntu:~/shell$
Simple shell 0.3 +
- Implement the
exit
built-in, that exits the shell - Usage:
exit
- You don’t have to handle any argument to the built-in
exit
Simple shell 0.4 +
- Implement the
env
built-in, that prints the current environment
julien@ubuntu:~/shell$ ./simple_shell
$ env
USER=julien
LANGUAGE=en_US
SESSION=ubuntu
COMPIZ_CONFIG_PROFILE=ubuntu
SHLVL=1
HOME=/home/julien
C_IS=Fun_:)
DESKTOP_SESSION=ubuntu
LOGNAME=julien
TERM=xterm-256color
PATH=/home/julien/bin:/home/julien/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
DISPLAY=:0
$ exit
julien@ubuntu:~/shell$