Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a command is in PATH/executable as process

Tags:

rust

I want to execute an external program via std::process::Command::spawn. Furthermore I want to know the reason why spawning the process failed: is it because the given program name doesn't exist/is not in PATH or because of some different error?

Example code of what I want to achieve:

match Command::new("rustc").spawn() {
    Ok(_) => println!("Was spawned :)"),
    Err(e) => {
        if /* ??? */ {
            println!("`rustc` was not found! Check your PATH!")
        } else {
            println!("Some strange error occurred :(");
        }
    }, 
}

When I try to execute a program that isn't on my system, I get:

Error { repr: Os { code: 2, message: "No such file or directory" } }

But I don't want to rely on that. Is there a way to determine if a program exists in PATH?

like image 981
Lukas Kalbertodt Avatar asked Jan 27 '16 19:01

Lukas Kalbertodt


1 Answers

You can use e.kind() to find what ErrorKind the error was.

match Command::new("rustc").spawn() {
    Ok(_) => println!("Was spawned :)"),
    Err(e) => {
        if let NotFound = e.kind() {
            println!("`rustc` was not found! Check your PATH!")
        } else {
            println!("Some strange error occurred :(");
        }
    }, 
}

Edit: I didn't find any explicit documentation about what error kinds can be returned, so I looked up the source code. It seems the error is returned straight from the OS. The relevant code seems to be in src/libstd/sys/[unix/windows/..]/process.rs. A snippet from the Unix version:

One more edit: On a second thought, I'm not sure if the licenses actually allows posting parts of Rust sources here, so you can see it on github

Which just returns Error::from_raw_os_err(...). The Windows version seemed more complicated, and I couldn't immediately find where it even returns errors from. Either way, it seems you're at the mercy of your operating system regarding that. At least I found the following test in src/libstd/process.rs:

Same as above: github

That seems to guarantee that an ErrorKind::NotFound should be returned at least when the binary is not found. It makes sense to assume that the OS wouldn't give a NotFound error in other cases, but who knows. If you want to be absolutely sure that the program really was not found, you'll have to search the directories in $PATH manually. Something like:

use std::env;
use std::fs;

fn is_program_in_path(program: &str) -> bool {
    if let Ok(path) = env::var("PATH") {
        for p in path.split(":") {
            let p_str = format!("{}/{}", p, program);
            if fs::metadata(p_str).is_ok() {
                return true;
            }
        }
    }
    false
}

fn main() {
    let program = "rustca"; // shouldn't be found
    if is_program_in_path(program) {
        println!("Yes.");
    } else {
        println!("No.");
    }
}
like image 165
jkiiski Avatar answered Oct 19 '22 12:10

jkiiski