# RShell Rust Shell. This is an attempt to create a simple shell in Rust, because why not. My starting point is [this article](https://www.joshmcguigan.com/blog/build-your-own-shell-rust/) which is excellent. I will start with the final form of a shell given in the article and try to reformat it: * Make it more easy to extend * Make it a lib I can also add basic features: * History * customizable prompt * Colours ? Later, I can even make something useful out of it. For example, I could make it so that it is configurable via a config file. I could make it the default terminal via ssh for some user and make it so the terminal opens directly in a docker container, or chrooted or smthing, as a basic form of honeypot. # Done * A basic history system. Writes to ~/history.rshell * Tests for this history system * Added the ! function # Todo * Add a function that gets the path of the history file, rather than copying it every time * Other tests for the rest of the features * Add a builtin history command * Get some sleep * Order the file (modules...) # Annotated process_line ```rust fn process_line(input: String) -> bool { // must be peekable so we know when we are on the last command let mut commands = input.trim().split(" | ").peekable(); //Will be used later, in the case of pipes let mut previous_command = None; //While have commands to process... while let Some(command) = commands.next() { //First command is divided... let mut parts = command.trim().split_whitespace(); //into a command... let mut command = parts.next().unwrap(); //and args let args = parts; //builtins : exit and cd are special match command { "cd" => { //If a dir is given, we use it by dereferencing //otherwise it is / let new_dir = args.peekable().peek().map_or("/", |x| *x); let root = Path::new(new_dir); if let Err(e) = env::set_current_dir(&root) { eprintln!("{}", e); } previous_command = None; }, "exit" => return true, "history" => { let stdout = Stdio::inherit(); println!("Insert history here"); }, //not builtin: we process it command => { //If we are at the first command (aka previous_command is None)... let stdin = previous_command.map_or( //We inherit, meaning stdin gets the stream from its parent //what I guess that means is that the child (our command) //inherits the file descriptors (stdin, stdout, stderr) //from the process of this very own rust program ! Stdio::inherit(), //otherwise, our input is the output from the last command //(from converts a ChildStdOut to a Stdio) |output: Child| Stdio::from(output.stdout.unwrap()) ); let stdout = if commands.peek().is_some() { // there is another command piped behind this one // prepare to send output to the next command Stdio::piped() } else { // there are no more commands piped behind this one // send output to shell stdout // the child inherits from the parent's file descriptor Stdio::inherit() }; //Run the command let output = Command::new(command) .args(args) .stdin(stdin) .stdout(stdout) //spawn : execute the command as a child process, returning a handle to it .spawn(); //Checking for errors //If it matched, previous_command becomes the output of this to be used //next loop match output { Ok(output) => { previous_command = Some(output); }, Err(e) => { previous_command = None; eprintln!("{}", e); }, }; } } } if let Some(mut final_command) = previous_command { // block until the final command has finished final_command.wait(); } return false; } ```