Allow std::process::Command to change args

I think the basic, common usage of std::process::Command looks something like this:

Command::new("git")
    .current_dir("/home/zooce/project")
    .args(["fetch", "-a"])
    .output()?;

But what if you wanted to use the same program (and probably current directory) for several steps? You have to create new commands for every step like this:

Command::new("git")
    .current_dir("/home/zooce/project")
    .args(["fetch", "-a"])
    .output()?;
Command::new("git")
    .current_dir("/home/zooce/project")
    .arg("pull")
    .output()?;
Command::new("git")
    .current_dir("/home/zooce/project")
    .args(["submodule", "udpate", "--init", "--recursive"])
    .output()?;

In this case it would be nice if Command allowed us to change its arguments but keep the program like this:

let mut git = Command::new("git");
git.current_dir("/home/zooce/project");

// just using `set_args` to differentiate from `args`
git.set_args(["fetch", "-a"]).output()?;
git.set_args(["pull"]).output()?;
git.set_args(["submodule", "udpate", "--init", "--recursive"]).output()?;

This might be kind of a dumb example, but I think it gets the point across.

Curious on others' thoughts about this...

I also want to note that std::process::Command already has a way to achieve this for environment variables with clear_env() so you can have a new set of environment variables but use the same instance of Command. So another possibility is to add a clear_args() function:

let mut git = Command::new("git");
git.current_dir("/home/zooce/project");

git.args(["fetch", "-a"]).output()?;
git.clear_args().arg("pull").output()?;
git.clear_args().args(["submodule", "update", "--init", "--recursive"]).output()?;

There is already a proposal to add mutation methods at Mutate command args by cehteh · Pull Request #87420 · rust-lang/rust · GitHub.

Yep, see that now! Thank you!!

A pattern that helps in cases like this is to use a small helper closure:

let git = || Command::new("git")
    .current_dir("/home/zooce/project");

git()
    .args(["fetch", "-a"])
    .output()?;
git()
    .arg("pull")
    .output()?;
git()
    .args(["submodule", "udpate", "--init", "--recursive"])
    .output()?;
2 Likes

Nice! That's pretty clever. That will help me in the meantime - thank you!!

So I realized that the following doesn't work because current_dir() would return a reference to a temporary value (because it's in a closure).

let git = || Command::new("git").current_dir("/home/zooce/project");

So I'm doing this instead:

let git = |args: &[&str]| Command::new("git")
    .current_dir("/home/zooce/project")
    .args(args)
    .output();

git(&["fetch", "-a"])?;
git(&["pull"])?;
git(&["submodule", "update", "--init", "--recursive"])?;
1 Like