From c66483932d658ae19b6cf823f54675238e0b57cb Mon Sep 17 00:00:00 2001 From: g1n Date: Tue, 20 Jul 2021 13:37:53 +0000 Subject: [PATCH] Add pipes (thanks to that article: https://www.joshmcguigan.com/blog/build-your-own-shell-rust/) --- src/main.rs | 148 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 53 deletions(-) diff --git a/src/main.rs b/src/main.rs index b989913..ed19080 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,53 +1,95 @@ -use std::io::{self, stdout, Write}; -use std::env; -use std::process::Command; -use std::path::Path; - -fn main() { - // Variables - let mut STATUS = 0; - - loop { - print!("$ "); - io::stdout().flush(); - - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); - - let mut parts = input.trim().split_whitespace(); - let command = parts.next().unwrap(); - let args = parts; - - match command { - "true" | ":" => { - STATUS = 0; - }, - "echo" => { - let args = args.peekable().peek().map_or("/", |x| *x); - if args.clone() == "$STATUS" { - println!("{}", STATUS); - } else { - println!("{}", args); - } - }, - "cd" => { - 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); - } - }, - "exit" => return, - command => { - let mut child = Command::new(command) - .args(args) - .spawn(); - - match child { - Ok(mut child) => { child.wait(); }, - Err(e) => { STATUS=1; eprintln!("{}", e); }, - }; - } - } - } -} +use std::env; +use std::io::{stdin, stdout, Write}; +use std::path::Path; +use std::process::{Child, Command, Stdio}; + + +fn main(){ + let mut STATUS=0; + + loop { + // use the `>` character as the prompt + // need to explicitly flush this to ensure it prints before read_line + print!("> "); + stdout().flush().unwrap(); + + let mut input = String::new(); + stdin().read_line(&mut input).unwrap(); + + // read_line leaves a trailing newline, which trim removes + // this needs to be peekable so we can determine when we are on then last command + let mut commands = input.trim().split(" | ").peekable(); + let mut previous_command = None; + + while let Some(command) = commands.next() { + + // everything after the first whitespace character is interpreted as args to the command + let mut parts = command.trim().split_whitespace(); + let command = parts.next().unwrap(); + let args = parts; + + match command { + "true" | ":" => { + STATUS = 0; + }, + "false" => { + STATUS = 1; + }, + "echo" => { + let args = args.peekable().peek().map_or("", |x| *x); + if args.clone() == "$STATUS" { + println!("{}", STATUS); + } else { + println!("{}", args); + } + }, + "cd" => { + // default to '/' as new directory if one was not provided + 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, + command => { + let stdin = previous_command + .map_or(Stdio::inherit(), + |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 + Stdio::inherit() + }; + + let output = Command::new(command) + .args(args) + .stdin(stdin) + .stdout(stdout) + .spawn(); + + 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().unwrap(); + } + + } +}