Giter Club home page Giter Club logo

minishell's Introduction

miniSHELL

Our own shell with @magueija


.Our task was to replicate into certain limits the behaviour of bash:

  • Show a prompt when waiting for a new command,
  • have a working history,
  • search and launch the right executable (based on the PATH variable or by using relative or absolute path),
  • implement builtins: echo -n, cd, pwd, export, unset, env, exit,
  • interpret behaviour of single ' and double " quotes, including behaviour of $ sing followed by environmental variable,
  • redirections: < (redirect input), > (redirect output), << (heredoc), >> (redirect output with append mode),
  • pipes | The output of each command in the pipeline is connected via a pipe to the input of the next command,
  • environment variables ($ followed by characters) should expand to their values,
  • $? should expand to the exit status of the most recently executed foreground pipeline,
  • ctrl-C ctrl-D ctrl-\ should work like in bash

How works shell?


1. Parse the input
2. Execute commands in order
Sounds like there is not that much to do, however the project took over a month to be completed.

Our soluion and challenges.

PARSING


This project had shown that sayig: "think twice code once" is true and elevated forward thinking ability is in price. Our 1st approach towars parsing was to save all input in two dimensional array. The moment we moved to pipes we realized that this will make our lifes very hard, as execve expects:

int execve(const char *pathname, char **const argv, char **const envp);

  1. command, 2. command and what follows untill pipe or redirection is encountered, 3. environmental variables

At this point we understood that better approach will be to create linked list, that contains all arguments untill redirection/pipe is found, will save type of redirection, we could also save output from access function (so we know if command is valid) as well as its path ex: 'ls' /bin/ls. Anytime redirection/pipe is found we create next node and link it. At this point executing it starts being pleasent.

typedef struct s_arguments
{
   char **args;
   char pipe_type[4];
   char *cmd_w_path;
   bool is_valid;
   bool special;
   struct s_arguments *next;
}    t_arguments;

PIPING


Every process (this is fancy name for a program) has its own redirection table, that looks this way:

standard input (stdin)    0
standard output (stdout)  1
standard error (stderr)   2

What do we know from it? Whenever you type anything in your command line, aka bash, zhs, whatever, your machine knows that input comes from file descriptor (fd) 0 (it is your keyboard) and it outputs to 1 that is the monitor. Nr 2 is working similar to 1 but it is used to show errors (later on when you redirect outputs with dup or dup2 and change your stdout to pipe writing end you may notice that your debugging printf is not working, the reason is that it actually works but put everything to pipe and you cannot see it, so you could use fprintf and try to print the message in standard error instead to see what is going on. But we will get there hopefully together :)).

Right now when we open new file with open process will create new fd with the lowest awaliable number, in this case 3. So when we open pipe with function pipe(int_array_of_size_2_to_hold_2_numbers_aka_fd's) we will add 2 new fd to our table:

standard input (stdin)    0
standard output (stdout)  1
standard error (stderr)   2
fd_pipe[0] - read part    3
fd_pipe[1] - write part   4

IMPORTANT! Please train your brain: read FROM, write TO. So we write TO the pipe and read FROM the pipe. It was very confusing to me, maybe only to me, as my brain would rather write from and read to but anyway. It already sat in.

IMPORTANT2! After you redirect output to the pipe (see example below) you have to close writing part of the pipe, otherwise you will get infinite loop (your pipe will be constantly waiting for the input, so by closing we are telling it that it has already got everything we wanted). Then YOU DO NOT CLOSE reading part of the pipe. Why? Oh man... (just kidding xd) You must leave it open so you in the next step (grep microshell) you can read from the pipe what you put there previously.

IMPORTANT3! Open pipe and close its fds in PARENT PROCESS! Anything that happens in child process dissapears after its end.

LET US CONSIDER THIS INPUT:

ls -l | grep minishell | wc -l

Pipe is simply sort of buffer:

ls -l -> goes to the pipe -> | output of ls -l is storred here instead of being displayed on your screen (a)| -> read from pipe, execute grep -> goes to the new pipe (b)| -> ls -l grep output is stored here | -> read from the pipe, execute wc -l -> write it to stdout (c).

So this should give you a number as a final output. (a) -> before going to grep, in your parent process you should close fd_pipe[1], as explained in IMPORTANT2! Before forking to execute grep we will open new pipe, so our table will look like this now:

standard input (stdin)    0
standard output (stdout)  1
standard error (stderr)   2
fd_pipe[0] - read part    3
__fd_pipe[1] - write part   4 - closed, so its not here anymore
fd_pipe[0] - read part    4
fd_pipe[1] - write part   5

Now you are probably wondering how we can have twice fd_pipe[0] and it is not creating confusion (I mean I would wonder). Pipe just opens new fd's and its more about numbers. If i write to 5 i will write to a "new pipe" if i read from 3 i will read from "old pipe"

(b) -> at this point, to avoid fd's leaks we close nr 3 because we not gonna need it anymore. And also nr 5 because we have already written to the pipe. And that's how it is rolling. wc -l has no pipe at the end, so we should enter child process with this table:

standard input (stdin)        0
standard output (stdout)      1
standard error (stderr)       2
fd_pipe[0] - old read part    4 - wc -l will read from here

(c) -> before we leave the program we close 4.

###END WORDS Of couse our task was much more complicated than this example. We had to handle for instance:

  • < input.txt ls -l | grep minishell | wc -l > output.txt
  • << a << b | wc-l > output.txt > output2.txt
  • execute executavel files ./a.out or whatever. we could create file, open it in VIM, edit, compile and execute.
  • and some more crazy ideas of the evaluators

But have in mind that it is not perfect. It passes the evaluation and much more, but you may always find a command that will not give desired output. What was actually interesting was that zsh (it is kinda fancy skin for bash) is giving different outputs than original bash. And original bash (according to my friend's info, so maybe not most reliable source ;p) took years to be written.

IMPORTANT239149023590! When you write your minishell and use zhs make yourself a favour and type bash before testing how does it work. :)

Thank you for your attention. I hope it clarified you a bit some doubts. If you would like to try our program, just git clone it and type make inside repository. If function readline shows problems then you must follow the instructions in bash script lib_setup.sh

##SOURCES

minishell's People

Contributors

psleziak42 avatar

Stargazers

Tomás Magueija Silva avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.