Is it possible to directly run cmd Windows commands on Rust code

Using Rust, is it possible to type cmd Windows commands and have it to execute?

Check out the documentation for std::process::Command.

Hi,

use std::process::Command;

let output = if cfg!(target_os = "windows") {
    Command::new("cmd")
            .args(&["/C", "echo hello"])
            .output()
            .expect("failed to execute process")
} else {
    Command::new("sh")
            .arg("-c")
            .arg("echo hello")
            .output()
            .expect("failed to execute process")
};

let hello = output.stdout;

Sorry I have pretty much no idea what is going on over here

cfg! what is this macro and what does it do?

Command::new("cmd") I am assuming this tells that we are using cmd.

.args(&["/C", "echo hello"]) What does the args function do and how come we are using references and was is it enclosed in [] brackets, is "/C" some sort of an escape character? If I want to add more commands other than echo hello then do I just put a comma or somethign?

I am going to assume that .output() is going to output the result onto the console.

Let's go through this code.

if cfg!(target_os = "windows") means that we are referring to some compile-time configuration through cfg! macro. In this case, the macro will expand to true on Windows and to false in all other cases. We're on Windows, so we continue for the first if branch.

Command::new requires one argument, which is the command name. That is, we're launching the cmd. Note that in most cases you don't need this - you can pass the program name to the Command::new directly; invoking shell explicitly might be necessary if we're about to use the shell builtin, i.e. some command which is interpreted by the shell itself and not as external program.

Then, we're passing some arguments to it through the args method on Command. The definition of this method looks a bit complicated, but in fact it is simple: it accepts anything which can be iterated, so that this iteration will yield something to be interpreted as OsStr. In particular, this method can accept a slice containing string slices, i.e. &[&str]; and we're creating this slice by taking a reference to an array. Each element of this array is passed as an argument to the command; result will be the same as if you've written cmd /C "echo hello" in the command interpreter itself (note the quotes - without them echo hello would be two arguments, not one).

Then, we're using the output method. As stated in the docs, this method capture the program output into two Vec<u8>, for stdout and stderr correspondingly, so that the program can analyze it. If you don't need this and want to pipe the output to the shell from which the main program was launched, you can use status method instead, which will yield only the exit code.

Last thing we're doing here is to unwrap the returned value: since the command may fail (for variety of reasons), output (and status) return Result, which can be expected to either return the inner value or panic with set message.

Is there anything left unclear? If so, where it is: in the explanation or in the documentation I've linked to?

3 Likes

Thanks for the explaination it cleared a lot of things.

fn main()
{
    use std::process::Command;
    Command::new("cmd")
    .args(&["/C", "echo hello"])
    .output()
    .expect("failed to execute process")
}

I want to not perform a check to determin what OS I am running on, so I just want to simply run and execute this because I want to design this just for Windows.

But why is this an error now suddenly?

Because now the output of the command is the last thing in, and therefore the result of, main. However, main is supposed to return (), so that's an error.

The simplest way to fix it is to put a semicolon at the end to discard the result, but that may not be what you need for your purposes, and certainly isn't what you want to do with a command like echo hello. You need to store or use the result if you want the output of the command (and all echo hello does is produce output). You can just ignore it with a semicolon if there is no relevant output, but some other parts of the Command API may be more relevant in that case.

What command are you actually trying to run?

1 Like

Oh I see lol I forgot to add that.

I just want to learn for fun as to how to run cmd commands through code.

Also another problem is that when I run the code I don't get any output. why is that?

args(&["/C", "echo hello"])

Also what is this "/C" mean?

Fair enough, I'll try to give a general explanation. The std::process::Command API allows for running a command in a separate process. This separate process has its own standard input, output and error streams, which the Rust program controls since it created the process. When you create a process with Command, you provide the name of the command you want to run (in this case "cmd") and can also set other things such as arguments, environment variables and redirecting the input/output. There are then 3 ways provided to start the process:

  • spawn() just creates the process and returns a std::process::Child that can be used to control the process. This is the most general way to manipulate the process generated by Command
  • output() runs the process until it finishes, then collects its standard output and error into a std::process::Output. This is useful if you just want the output of a command, without interacting with it in between.
  • status() runs the process until it finishes, with standard input, output and error streams inherited from the parent unless otherwise specified, and collects its exit status into a std::process::ExitStatus. This is useful when you just want to run a command without interacting with its input or output.

Note that the creation of a process can fail, so all these methods return a Result.

In your case, the process produces output, but since you use output() it is collected into a struct that you then discard. Using status() instead is likely to give the behaviour that you're expecting (using Rust's output for the process' output and not capturing it in the program).

The full command being run here is effectively cmd /C "echo hello". This runs the command shell cmd. the /C tells it just to run the given command in the shell instead of opening up for user input. The echo hello` is the command being run in the shell, which just sends "hello" to standard output.

1 Like

Quoting myself:

So, if you want to get the output, you must either launch the command in another way (like using status), or use the output's return value.

It is the argument to the cmd, so you should look for its documentation. I've found the relevant part in the docs for Windows Server:

So, the whole command means "cmd, execute this command [echo hello in your case] and exit".

2 Likes

Hi @jameseb7 and @Cerberuser THanks for your help and explaining it to me :slight_smile:

fn main()
{
    use std::process::Command;
    Command::new("cmd")
    .args(&["/C", "echo Hello", "pause"])
    .status()
    .expect("failed to execute process");
}

When I run this, it does output but like how come it outputs Hello pause. If I put the pause command first then it will run the pause command. Do you guys know how do I run multiple cmd commands in one process?

You'd have to structure your command input like so (In total)

cmd /C "echo Hello & pause"

So, like so:

Command::new("cmd")
    .args(&["/C", "\"echo Hello & pause\""])
    .status()
    .expect("Failed to execute");
1 Like
'\"echo Hello & pause\"' is not recognized as an internal or external command,
operable program or batch file.

I get this error ::confused:

No need for quotes around the second argument, they are necessary only for command parser. .args(&["/C", "echo Hello & pause"]) should work.

2 Likes
fn main()
{
    use std::process::Command;
    Command::new("cmd")
    .args(&["/C", "echo Hello & 
    pause

    "])
    .status()
    .expect("Failed to execute");
}

I want to seperate each command like this. But it would not work. If I have a huge cmd commands I want to execute I don't want to shove everything in one line. How do I fix this?

I'm not sure that this is possible on the Rust side. The best shot might be to learn how to use the batch files (and call this file from Rust instead of invoking cmd).

I know how to write up a batch script but I wanted to compile it into binary so I thought Rust would be ideal for me.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.