Search code examples
cygwinrust

Executing `find` using `std::process::Command` on cygwin does not work


When I try to call the find command from a Rust program, either I get a FIND: Invalid switch or a FIND: Parameter format incorrect error.

find works fine from command line.

echo $PATH

/usr/local/bin:/usr/bin:/cygdrive/c/Windows/system32:/cygdrive/c/Windows:.....

The file I am searching for (main.rs) exists.

use std::process::{Stdio,Command};
use std::io::{Write};

fn main() {
    let mut cmd_find = Command::new("/cygdrive/c/cygwin64/bin/find.exe")
        .arg("/cygdrive/c/cygwin64/home/*")
        .stdin(Stdio::piped())
        .spawn()
        .unwrap_or_else(|e| { panic!("failed to execute process:  {}", e)});

    if let Some(ref mut stdin) = cmd_find.stdin {
        stdin.write_all(b"main.rs").unwrap();   
    }       

    let res = cmd_find.wait_with_output().unwrap().stdout;  
    println!("{}",String::from_utf8_lossy(&res));
}

./find_cmdd.exe

thread '<main>' panicked at 'failed to execute process: The system cannot find the file specified. (os error 2)', find_cmdd.rs:12

I have also tried the following option,

let mut cmd_find = Command::new("find").....

for which I get FIND:Invalid switch error.

I do not have the luxury of renaming/copying the find.exe to another location.


Solution

  • Cygwin basically doesn't exist when you are running a program via Command. Executing a process uses the operating system's native functionality; in the case of Windows that's CreateProcessW.

    That means that:

    1. The PATH variable set by your cygwin shell may or may not mean anything when starting a process.
    2. The directory structure with /cygdrive/... doesn't actually exist in Windows; that's an artifact.

    All that said, you have to use Windows-native paths:

    use std::process::{Stdio, Command};
    use std::io::Write;
    
    fn main() {
        let mut cmd_find = Command::new(r#"\msys32\usr\bin\find.exe"#)
            .args(&[r#"\msys32\home"#])
            .stdin(Stdio::piped())
            .spawn()
            .unwrap_or_else(|e| panic!("failed to execute process:  {}", e));
    
        if let Some(ref mut stdin) = cmd_find.stdin {
            stdin.write_all(b"main.rs").unwrap();
        }
    
        let res = cmd_find.wait_with_output().unwrap().stdout;
        println!("{}", String::from_utf8_lossy(&res));
    }
    

    As a side note, I have no idea what piping standard input to find does; it doesn't seem to have any effect for me on Msys2 or on OS X...