Using clap to get file name expansion

Hi all,

I didn't find in the clap documentation of way of managing both cli flags and shell file expansion.

I mean I want to be able to pass flag (e.g: -x) and/or only use a file glob pattern (e.g: *.jpg). Filename expansion occurs (on UNIX) before calling the executable, and the clap crate allows you to use mutliple values but with an option (only?).

I can use the std::env and collect the arguments but doing this I lose the clap facilities.

To be more precise:

$ myapp -x *.jpg
$ myapp *.jpg

Any hint welcome :slight_smile:

At the point when clap runs (or anything else in your program) it's already too late to distinguish between expanded and non-glob-expanded args (*.jpg vs multiple file names entered by hand). There's no trace of the original input, and you can't get it, so there's nothing any library can do to act on this..

You can ask uses to run myapp -x "*.jpg" or myapp \*.jpg to get "*.jpg" inside the app. You won't ever know whether someone has typed myapp *.jpg or myapp image1.jpg image2.jpg.

@kornel Thanks for your answer, that's what I was thinking.

I guess the only solution is to not use clap and manage arguments using std::env.

std::env can't help you too. Expansion is performed by the shell, before your code even starts.

2 Likes

Yes, and the fun fact is that expansion is a feature of particular shell implementations. Theoretically user could use some other shell that doesn't support expansion, or has a completely different syntax for it. And it's possible to execute an executable without involving any shell at all.

Such as windows cmd.exe, apparently!? This blows my mind, a little.

Yes, I wrote a crate that hides this difference:

2 Likes

@Cerberuser Thanks for your comment, I know that. I meant managing the flag by myself, not using the clap crate :wink:

If I'm understanding right, this should work

    let matches = App::new("some name")
        .arg(
            Arg::with_name("option")
                .short("x")
                .takes_value(true)
                .multiple(true),
        )
        .arg(Arg::with_name("files").multiple(true))
        .get_matches();