vvv
vvv

Reputation: 186

How to get the name of current program without the directory part?

In Bash this would be ${0##*/}.

use std::env;
use std::path::Path;

fn prog() -> String {
    let prog = env::args().next().unwrap();
    String::from(Path::new(&prog).file_name().unwrap().to_str().unwrap())
}

fn main() {
    println!("{}", prog());
}

Is there a better way? (I particularly dislike those numerous unwrap()s.)

Upvotes: 9

Views: 8378

Answers (3)

imbolc
imbolc

Reputation: 1833

Just one more Option version :)

fn prog() -> Option<String> {
    std::env::current_exe()
        .ok()?
        .file_name()?
        .to_str()?
        .to_owned()
        .into()
}

Upvotes: 7

oli_obk
oli_obk

Reputation: 31163

You can also get rid of the unwraps and still report all error causes properly (instead of munching them into a "something failed" None). You aren't even required to specify the full paths to the conversion methods:

fn prog() -> Result<String, ProgError> {
    let path = try!(env::current_exe());
    let name = try!(path.file_name().ok_or(ProgError::NoFile));
    let s_name = try!(name.to_str().ok_or(ProgError::NotUtf8));
    Ok(s_name.to_owned())
}

Together with the future questionmark operator this can also be written as a single dot call chain:

fn prog() -> Result<String, ProgError> {
    Ok(env::current_exe()?
        .file_name().ok_or(ProgError::NoFile)?
        .to_str().ok_or(ProgError::NotUtf8)?
        .to_owned())
}

Of course this has the prerequisite of the ProgError type:

use std::io::Error;

#[derive(Debug)]
enum ProgError {
    NoFile,
    NotUtf8,
    Io(Error),
}

impl From<Error> for ProgError {
    fn from(err: Error) -> ProgError {
        ProgError::Io(err)
    }
}

try it out on the Playground

Upvotes: 7

Shepmaster
Shepmaster

Reputation: 430310

If you don't care about why you can't get the program name, you can handle all the potential errors with a judicious mix of map and and_then. Additionally, return an Option to indicate possible failure:

use std::env;
use std::path::Path;
use std::ffi::OsStr;

fn prog() -> Option<String> {
    env::args().next()
        .as_ref()
        .map(Path::new)
        .and_then(Path::file_name)
        .and_then(OsStr::to_str)
        .map(String::from)
}

fn main() {
    println!("{:?}", prog());
}

If you wanted to follow delnan's awesome suggestion to use std::env::current_exe (which I just learned about!), replace env::args().next() with env::current_exe().ok().


If you do want to know why you can't get the program name (and knowing why is usually the first step to fixing a problem), then check out ker's answer.

Upvotes: 13

Related Questions