oconnor663 / duct.rs

a Rust library for running child processes
MIT License
795 stars 34 forks source link

Globbing doesn't work #110

Closed azzamsa closed 1 year ago

azzamsa commented 1 year ago

Hi.

I want remove all files inside a directory. With the parent directory intact. With bash, I can use rm -rf /home/user/.tmp/* (note asterisk). With duct, it doesn't work.

    cmd!("rm", "-rf", "/home/user/.tmp/*").run()?;  // ⚠️, doesn't work

My only option is to remove the .tmp directory and recreate it again.

My current solution is:

    let paths = fs::read_dir(format!("{}/.tmp", env::var("HOME")?))?;
    for path in paths {
        cmd!("rm", "-rf", path?.path()).run()?;
    }

Is the globbing feature is not supported, or am I missing something?

Thanks a lot for duct :heart:

oconnor663 commented 1 year ago

You're right, globbing doesn't work. The reason is that globbing is something your shell does for you, but duct isn't going through your shell. For the same reason, special characters like > and < won't work (though duct has methods that stand in for those).

If you want to go through the shell to get globbing to work, you have two options. You can explicitly pass a command to the shell, using e.g. Bash's -c flag. That looks like this:

cmd!("bash", "-c", "ls *.txt").run()?;

Alternatively, you can take a look at the duct_sh crate, which takes care of that for you:

duct_sh::sh("ls *.txt").run()?;

In both those cases, your shell command has to be "one big string", so you do sometimes need to worry about whitespace and escaping issues. That's the main reason duct doesn't talk to the shell by default.

On the other hand, doing the loop in Rust like you did above is totally reasonable, and I'd say it's cleaner. Note that the standard library provides std::fs::remove_file, so you could even get rid of the rm command. And if you ever need globbing in regular Rust code, you might want to take a look at the glob or wax crates. (Full disclosure, wax was made by a friend of mine 😁)

azzamsa commented 1 year ago

special characters like > and < won't work (though duct has methods that stand in for those).

Oh, I see. That's why instead of pushd target >/dev/null, we need to use cmd!(foo).dir(target).run()?

Note that the standard library provides std::fs::remove_file

Nice, I will port my code to use it.


Thank you so much for your concise and thorough explanation. I am really thank you for duct. Now I can port all my previous bash scripts to Rust, and it acts the same way as a shell script. It is unnoticeable from a user's perspective.

Thanks a lot for duct! :heart:

oconnor663 commented 1 year ago

You're very welcome, I'm glad it's working for you 😁