Std::process on windows is escaping raw literals which causes problems with chaining commands

Here’s a test example using a cmd /c + env workaround. I leave it here for anyone who needs a temp workaround until we work out a long term answer

NOTE: I have reworked this to use powershell because cmd complains about network paths. This was more difficult that I had anticipated so I’ve put the updated version here for people who aren’t as versed with powershell’s eccentricities

//mod windows_runner;


fn main() {
    let quote_test: &str = r#" one" two"" three""" four"""" five""""" "#;
    {   // to show you can collect stdout
        let stdout_test: String = windows_runner::run("write-host", quote_test, ""); 
        println!("{}",stdout_test);
    }
    {   // to check it passes stdin correctly, also shows you can call without argument
        let stdin_test: String = windows_runner::run("nslookup",r#""#,"google.com\nexit\n");
        println!("{}",stdin_test);
    }
    {   // to check (with procexp) that the arguments actually pass exactly as given (including without surrounding quotes), though it does add one extra space between program and arguments
        windows_runner::run("notepad", quote_test, "");
    }
}


mod windows_runner{
    use std::{thread, time, str};
    use std::process::{Command, Stdio};
    use std::io::Write;
    pub fn run (program:&str,arguments:&str,stdin:&str) -> String /*(String,String)*/ {
        let launcher = "powershell.exe";
        let build_string: String;
        {
            if arguments.trim() == "" { // no arguments (powershell gets confused if you try to execute a program with an empty array as the argument set)
                build_string = format!(r#"& '{}'"#,program);
            }
            else {
                let mut arguments_reformatting: Vec<&str> = Vec::new();
                for argument in arguments.split(" ") {
                    arguments_reformatting.push(argument);
                }
                let arguments_reformatted = arguments_reformatting.join("','");
                build_string = format!(r#"& '{}' @('{}')"#,program,arguments_reformatted); // powershell digests: & 'pro gram' @('argument1','argument2') => "pro gram" argument1 argument2
            }
        }
        let launch_command: &[String] = &[build_string];

        let mut child = Command::new(launcher)
            .args(launch_command)
            .stdout(Stdio::piped())
            .stdin(Stdio::piped()) // disable this if you want the user to be able to speak with the child instead of doing it yourself
            /*.stderr(Stdio::piped())*/ // if you want to collect stderr instead of displaying to user
            .spawn()
            .expect("failed to run child program");

        {   // send stdin, disable this if you want the user to be able to speak with the child instead of doing it yourself
            let stdin_handle = child.stdin.as_mut().expect("Failed to get stdin");
            stdin_handle.write_all(stdin.as_bytes()).expect("Failed to write to stdin");
        }

        // would you kindly wait for the child to finish
        let check_every = time::Duration::from_millis(10);
        loop {
            match child.try_wait() {
                Ok(Some(_status)) => {break;},  // finished running
                Ok(None) => {}                  // still running
                Err(e) => {panic!("error attempting to wait: {}", e)},
            }
            thread::sleep(check_every);
        }

        let output = child
            .wait_with_output()
            .expect("failed to wait on child");
        let stdout: String = String::from_utf8_lossy(&output.stdout).to_string();

        
        /*{ // if you want to collect stderr instead of displaying to user
            let stderr: String = String::from_utf8_lossy(&output.stderr).to_string();
            (stdout,stderr)
        }*/

        stdout
    }
}