Search code examples
rustsshcommandscprust-cargo

How to get all of the process::Command output


So I am trying to reconstruct a bash script I made using rust (allows for hashing and for more customization). I cannot seem to get the output of the command to my terminal though and I just can't seem to figure out what I'm doing wrong. I have tried piping stdout in so many different ways already and I'm at a stand still.

I would like to see this part in the command line as well but just have it all being auto filled (and yes I know ssh keys exist lol)

user@server's password:
ibits.c                                                                  100% 1137   271.2KB/s   00:00
fn auto_scp(flags: FlagOptions, f_path: String) {
    let info = open_config();
    let mut args: Vec<String> = vec![];
    if flags.is_folder {
        args.push(String::from("-r"))
    }
    if flags.d_load {
        let path = info.rmt_hst+":"+&f_path;
        // println!("{}",path);
        args.push(path);
        args.push(info.ddest);
    } else {
        let path = info.rmt_hst+":"+&info.udest;
        // println!("{}",path);
        args.push(f_path);
        args.push(path);
    }
    let mut com = process::Command::new("/bin/scp")
                                  .args(args)
                                  .stdout(process::Stdio::piped())
                                  .output()
                                  .expect("stuff");
    // io::stdout().write_all(b"hello world\n");
                                  // .expect("Could not output");
    // let stdout = String::from_utf8(com.stdout).unwrap();
    println!("stdout: {}", String::from_utf8(&com.stdout));
    // io::stdout().write_all(&outc.stdout).unwrap();
    println!("{}", stdout);
    return;
}

Solution

  • For the stdout to be visible to the user, the easiest is not to capture it yourself but simply let it through, which is what happens if you don't call .stdout nor .output but simply .status or .spawn:

    fn main() {
        std::process::Command::new ("/usr/bin/echo")
            .arg ("Hello world")
            .status()
            .unwrap();
    }
    

    Playground

    If you want to pass input to the child process (e.g. a password), you need to call .stdin (Stdio::piped()) before spawning the child (with .spawn), which will give you a Child handle whose stdin field can be used to write to the child:

    use std::io::Write;
    use std::process::{ Command, Stdio, };
    
    fn main() {
        let mut child = Command::new ("/usr/bin/cat")
            .stdin (Stdio::piped())
            .spawn()
            .unwrap();
        let mut pipe = child.stdin.take().unwrap();
        pipe.write_all ("hello world".as_bytes()).unwrap();
        drop (pipe); // So that `cat` will exit
        child.wait().unwrap();
    }
    

    Playground