Skip to content

dblarons/haskell-shell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A Shell in Haskell

Note: This was the first project I attempted in Haskell. As such, there are many, many flawed design choices and instances where I could have made better use of monads, etc. A more recent (though incomplete) project is my torrent client, located here.

Forward

This project was created as part of a final project for the Operating Systems class at Vanderbilt University. One of the project choices was to implement a simple shell that supported a variety of commands (described below). I used Haskell because I thought it would be interesting to apply functional programming to a type of program that generally requires keeping a lot of state and doing tons of IO. The result is far from perfect, but the insights I had into programming in Haskell, programming with state, and my own design capabilities were well worth the experience.

Built-In Commands

  • cd: Change directory is a built-in because it must be run in the parent process. Running it in the child is useless as the parent will still remain in the same directory as before when the child process is killed.

  • exit: Exit needs to be a built-in for the same reasons as cd.

  • help: Help is a built-in because its functionality is specific to my shell.

  • | (pipe operator): The pipe operator is a builtin. It takes the text output from one command and passes it to another command. Built-in commands can also be piped to/from.

  • > (I/O redirection operator): I/O redirection is implemented in one direction only. It allows the user to take stdout and redirect it into a file. It can also redirect between files.

  • bindkey: Allows the user to shoose between Emacs and Vi editing line editing modes.

  • export: Used for making new environment variables or modifying existing ones.

  • printenv: Print all environment variables that have been set.

External Commands

As long as a program is on your PATH, it can be run within this shell.

Config File

A config dotfile can be used with this shell. It should be named .hashrc and be placed in the users home directory (e.g. ~/.hashrc).

The export and bindkey commands can be used in the definition of the .hashrc file. It does not support any shell scripting.

Autocomplete

The shell supports autocomplete. Press tab to complete a command.

Design

One of the most interested aspects of my design is the pipeline I parsed a shell command into. This pipeline consists of a few typeclasses (interfaces in OO lingo) that can can be operated on uniformly. This allowed me to treat piping, file redirection, built-in, and external commands the same when executing them. These commands get built up into a linked list of sorts, which can then be recursively evaluated from left to right, passing the output of each command as the input to the next command.

One of the problems with this model was a result of Haskell's lazy design. The next command in the sequence would begin running before the prior one had finished. I unfortunately had to explicitly wait for the child process for each command to end before evaluating the next command in the sequence.

Background Tasks

Support for background tasks was broken when I implemented piping. I have yet to fix it.

Known Problems

In addition to background tasks not yet working, a few other (known) issues exist in this shell:

  1. Running the shell inside of itself results in it recursively calling itself into infinity. I do not recommend that you try this.

  2. Running the shell as your user shell breaks pipelining. It also breaks Tmux and Vim.

  3. The shell does not play nicely with Vim's Syntastic plugin (and therefore YouCompleteMe as well).

Project setup

  1. Clone this repo.

  2. Install the Haskell Platform

  3. Install readline bindings:

    a. brew install readline

    b. cabal install readline --extra-include-dirs=/usr/local/Cellar/readline/6.3.8/include/ --extra-lib-dirs=/usr/local/Cellar/readline/6.3.8/lib/ --configure-option=--with-readline-includes=/usr/local/Cellar/readline/6.3.8/include/ --configure-option=--with-readline-libraries=/usr/local/Cellar/readline/6.3.8/lib/

  4. Run cabal configure to configure the project without tests and cabal configure --enable-tests to configure with tests.

  5. Run ghci to start an interpreter.

  6. Run :l Main.hs in interpreter to begin interpreting the Main file.

  7. Run main in interpreter after :l Main.hs to begin the shell.

How project should be used.

  1. ls -al | grep foo | grep bar

  2. export PATH=$PATH:/foobar

  3. printenv

  4. bindkey -v or bindkey -e

  5. export CS281=awesome

  6. help

About

A shell written in Haskell

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published