I didn't try this on Windows, but this is what I came up with
use std::process::Command;
fn main() {
let output = run_command("dir", &[""]);
println!("{}", output)
}
fn run_command(cmd: &str, args: &[&str]) -> String {
let (exe, flag) = if cfg!(target_os = "windows") {
("cmd", "/C")
} else {
("sh", "-c")
};
let output = Command::new(exe)
.arg(flag)
.arg(cmd)
.args(args)
.output()
.unwrap()
.stdout;
String::from_utf8(output).unwrap()
}
As for what issues the original code had, the Windows code used args
but other platforms used arg
, leading to an error that tries to say that an array cannot be used as a single argument:
error[E0277]: the trait bound `[&str; 2]: AsRef<OsStr>` is not satisfied
--> src/main.rs:16:32
|
16 | Command::new("sh").arg(&["-c", &*cmd_with_args])
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsRef<OsStr>` is not implemented for `[&str; 2]`
|
= help: the following implementations were found:
<[T; N] as AsRef<[T]>>
<[T] as AsRef<[T]>>
= note: required because of the requirements on the impl of `AsRef<OsStr>` for `&[&str; 2]`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `sandbox-rs`
To learn more, run the command again with --verbose.
The arg
call tries to turn its argument into a string (OsStr, to be precise), but it can't do that with an array. That fixed, the next error is
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:14:9
|
13 | let output = if cfg!(target_os = "windows") {
| __________________-
14 | | Command::new("cmd").args(&["/C", &*cmd_with_args])
| | ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
15 | | } else {
| | - temporary value is freed at the end of this statement
16 | | Command::new("sh").args(&["-c", &*cmd_with_args])
17 | | }
| |_____- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:16:9
|
13 | let output = if cfg!(target_os = "windows") {
| __________________-
14 | | Command::new("cmd").args(&["/C", &*cmd_with_args])
15 | | } else {
16 | | Command::new("sh").args(&["-c", &*cmd_with_args])
| | ^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
17 | | }
| | -
| | |
| |_____temporary value is freed at the end of this statement
| borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0716`.
error: could not compile `sandbox-rs`
To learn more, run the command again with --verbose.
What this error is saying that the Command
you're making is dropped at the end of the if-block, but you're still trying to use it after that. The problem is that the args
method takes and returns a mutable borrow of a command, not a full command struct. The error may make more sense if we write the if block out like this
let output = if cfg!(target_os = "windows") {
let mut command = Command::new("cmd");
let mutable_borrow_of_command = command.args(&["/C", &*cmd_with_args]);
mutable_borrow_of_command
// command is dropped here...
} else {
let mut command = Command::new("sh");
let mutable_borrow_of_command = command.args(&["-c", &*cmd_with_args]);
mutable_borrow_of_command
// command is dropped here...
}
.stdout(Stdio::piped()) // but we're trying to keep using it here
.output()
.unwrap()
.stdout;
This is functionally the same as your code, but hopefully makes the issue more clear.