Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding executable in PATH with Rust

Tags:

rust

In Python I can:

from distutils import spawn

cmd = spawn.find_executable("commandname")

I tried something like the code below, but it it assumes you're on unix-like system with /usr/bin/which available(also it involves execution of external command which I want to avoid):

use std::process::Command;

let output = Command::new("which")
                      .arg("commandname")
                      .unwrap_or_else(|e| /* handle error here */)

What is the simplest way to do this in Rust?

like image 200
baldrs Avatar asked May 28 '16 12:05

baldrs


Video Answer


2 Answers

I found a crate that solves the problem: which. It includes Windows support, even accounting for PATHEXT.

like image 165
Aankhen Avatar answered Sep 22 '22 02:09

Aankhen


I'd probably grab the environment variable and iterate through it, returning the first matching path:

use std::env;
use std::path::{Path, PathBuf};

fn find_it<P>(exe_name: P) -> Option<PathBuf>
    where P: AsRef<Path>,
{
    env::var_os("PATH").and_then(|paths| {
        env::split_paths(&paths).filter_map(|dir| {
            let full_path = dir.join(&exe_name);
            if full_path.is_file() {
                Some(full_path)
            } else {
                None
            }
        }).next()
    })
}

fn main() {
    println!("{:?}", find_it("cat"));
    println!("{:?}", find_it("dog"));
}

This is probably ugly on Windows as you'd have to append the .exe to the executable name. It should also potentially be extended to only return items that are executable, which is again platform-specific code.

Reviewing the Python implementation, it appears they also support an absolute path being passed. That's up to you if the function should support that or not.

A quick search on crates.io returned one crate that may be useful: quale, although it currently says

currently only works on Unix-like operating systems.

It wouldn't surprise me to find out there are others.


Here's some ugly code that adds .exe to the end if it's missing, but only on Windows.

#[cfg(not(target_os = "windows"))]
fn enhance_exe_name(exe_name: &Path) -> Cow<Path> {
    exe_name.into()
}

#[cfg(target_os = "windows")]
fn enhance_exe_name(exe_name: &Path) -> Cow<Path> {
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;

    let raw_input: Vec<_> = exe_name.as_os_str().encode_wide().collect();
    let raw_extension: Vec<_> = OsStr::new(".exe").encode_wide().collect();

    if raw_input.ends_with(&raw_extension) {
        exe_name.into()
    } else {
        let mut with_exe = exe_name.as_os_str().to_owned();
        with_exe.push(".exe");
        PathBuf::from(with_exe).into()
    }
}

// At the top of the `find_it` function:
// let exe_name = enhance_exe_name(exe_name.as_ref());
like image 38
Shepmaster Avatar answered Sep 26 '22 02:09

Shepmaster