Skip to content
/ shelly Public

A simple shell written in C. Supports Chains, Pipes and Redirects.

Notifications You must be signed in to change notification settings

M0r13n/shelly

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation



  _____ _          _ _       
 / ____| |        | | |      
| (___ | |__   ___| | |_   _ 
 \___ \| '_ \ / _ \ | | | | |
 ____) | | | |  __/ | | |_| |
|_____/|_| |_|\___|_|_|\__, |
                        __/ |
                       |___/ 

Shelly - A simple shell written in C

Shelly is a very basic shell that I wrote to teach myself more about C and POSIX processes.

Features

  • Supports builtin commands cd, help and exit
  • Is able to launch processes
  • Supports whitespace escaping
  • History
  • Autocompletion (filenames and built in commands)
  • Piping
  • Redirection

Compile

Manually

$ mkdir -p ./build && cd ./build && cmake ..  && make ; cd .. ; 

Using build.sh

$ chmod +x ./scripts/build.sh && ./scripts/build.sh

Run

After you build shelly you can can it:

$ ./build/shelly

Run Tests

Manually

$ mkdir -p ./build && cd ./build && cmake ..  && make && ctest ; cd .. ; 

Using test.sh

$ chmod +x ./scripts/test.sh && ./scripts/test.sh  ;

Examples

>>> ls -l | wc -c > somefile.out
>>> ls -l | grep shelly > somefile.out
>>> tail -2 somefile.out > somefile.out
>>> echo "Hello Over there" > somefile.out
>>> cat main.c | grep a > somefile.out
>>> curl -L google.com > google.html

Memory leaks ?

~ leaks 36673         
Process:         shelly [36673]
Path:            /Users/USER/*/shelly
Load Address:    0x109943000
Identifier:      shelly
Version:         ???
Code Type:       X86-64
Parent Process:  zsh [1331]

Date/Time:       2020-02-09 13:06:38.209 +0100
Launch Time:     2020-02-09 13:05:12.482 +0100
OS Version:      Mac OS X 10.15.2 (19C57)
Report Version:  7
Analysis Tool:   /usr/bin/leaks

Physical footprint:         508K
Physical footprint (peak):  508K
----

leaks Report Version: 4.0
Process 36673: 279 nodes malloced for 184 KB
Process 36673: 0 leaks for 0 total leaked bytes.

How does it work?

The following is a small and incomplete writeup for me to consolidate what i've learned.

Autocompletion

When the program is started, main.c is executed.

First of all initialize_readline(); is called once. This function is used to extend the default behaviour of readlines autocompletion.

Normally the GNU library readline ships with autocompletion for filenames. This is very helpful, but not enough. I want Shelly to be able to autocomplete at least all of it's built in commands. Therefore it is needed to set rl_attempted_completion_function = (CPPFunction *) command_name_completion; during initialization. That function is located inside input.c. command_name_completion takes three arguments:

  1. text: This text should be autocompleted
  2. START and END: START and END show the region of text to complete.

This function checks if start is zero (assuming that only the first word can be a command). If the former is true it searches through the builin commands and tries to find matching strings for autocompletion. It uses readlines internal function completion_matches that uses a generator function to generate the list of possible matches. In this case the generator function command_name_generator is called repeatedly from completion_matches. The single purpose of this method is to compare the partial text to all built in commands. It takes the text and the current state (number of iterations so far) as arguments and always returns a string. Text is the partial word to complete. When there are no possibilities left it returns ((char *) NULL);.

Note: If command_name_completion is called for any other than the first word, it returns (char **) NULL;. This enables fallback to filename completion built into readline.

Pipes

A pipe is used to connect the standard input stdin of one process to the standard output stdout of another. Pipes are used since early versions of UNIX. They are often used like this:

$ ls | sort | grep Music

The above chain of commands pipes the output of ls to sort and then from sort to grep.

Some facts about pipes:

  • Pipes are unidirectional. A process can either write to or read to from a pipe, but not both. This is called a half duplex pipe. If we want to create a bidirectional (full duplex) pipe, we need two pipes.sh
  • Pipes can only be established between process with the same parents. Normally a pipe is created by calling fork() from a parent process. Which leaves two processes (parent and child)
The pipe syscall

A pipe is created with the following syscall:

#include <unistd. h.>
int pipe( int fd[2] );

The function returns 0 on success and -1 on error. Note that there are two file descriptors:

  • fd[0] is used for reading from the pipe
  • fd[1] is used for writing to the pipe

Having those file descriptors inside the same process is not terrible useful. This way to process can send data to itself. Instead we fork after we created the pipe. This way both processes have local copies of fd[0] and fd[1]. The only thing left to do, is do signal which process should write and which process should read. This is done by closing the unused descriptor.

Parent process writes / child reads : Parent closes fd[0] and child closes fd[1].

Good Reads

About

A simple shell written in C. Supports Chains, Pipes and Redirects.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published