DEV Community

BC
BC

Posted on

Day23:Call shell command - 100DayOfRust

I want to call some shell commands from Rust. In Python it is as easy as:

import os
os.system("ls -alh")
Enter fullscreen mode Exit fullscreen mode

In Rust, we can use the std::process::Command function.

ls example

use std::process::Command;
use std::str;


fn main() {
    // ls
    let output = Command::new("ls")
                         .arg("-alh")
                         .output()
                         .unwrap();
    let output = str::from_utf8(&output.stdout).unwrap();
    println!("{}", output);
}
Enter fullscreen mode Exit fullscreen mode

Run it we can get something like:

total 8
drwxr-xr-x  3 0xbf  staff    96B Dec 10 22:23 .
drwxr-xr-x  8 0xbf  staff   256B Dec 10 22:23 ..
-rw-r--r--  1 0xbf  staff   288B Dec 10 22:27 main.rs
Enter fullscreen mode Exit fullscreen mode

cp example

Suppose we have folder source and target, under source there is a a.log file and another folder b. The file structure like this:

|- source
    |- a.log
    |- b
|- target
Enter fullscreen mode Exit fullscreen mode

Now we want to recursively copy all files under source to target:

use std::process::Command;


fn main() {
    // cp -r source target
    let status = Command::new("cp")
                         .arg("-r")
                         .arg("source")
                         .arg("target")
                         .status()
                         .unwrap();
    println!("cp status: {}", status);
}
Enter fullscreen mode Exit fullscreen mode

If we run this, we could see it copied the source folder under target, like this:

|- source
    |- a.log
    |- b
|- target
    |- source
        |- a.log
        |- b
Enter fullscreen mode Exit fullscreen mode

But this is not what I want, I want to only copy file a.log and folder b to the target folder, not the source folder itself. We know in shell we can simply do cp -r source/* target, so let's try to change the code to:

let status = Command::new("cp")
                         .arg("-r")
                         .arg("source/*")
                         .arg("target")
                         .status()
                         .unwrap();
Enter fullscreen mode Exit fullscreen mode

Now if we run the code, we will actually get an error:

cp: source/*: No such file or directory
cp status: exit code: 1
Enter fullscreen mode Exit fullscreen mode

The program treated source/* as a single file, which in this case, we don't have a file named as source/*. It doesn't work.

So what's the solution? Well, we can use sh -c "cp -r source/* target" !

let status = Command::new("sh")
                         .arg("-c")
                         .arg("cp -r source/* target")
                         .status()
                         .unwrap();
println!("cp status: {}", status);
Enter fullscreen mode Exit fullscreen mode

Now if we run the code, we can see cp status: exit code: 0 print out, and our file structure like this:

|- source
    |- a.log
    |- b
|- target
    |- a.log
    |- b
Enter fullscreen mode Exit fullscreen mode

It works!

P.S, While Rust has fs::copy function to copy one single file, it doesn't have function to recursively copy a folder to another. Using the method we just introduced can achieve such goal. Another way is using the fs_extra crate, but if your program only run in Linux/Unix system, I think directly call shell cp command is simpler.

Top comments (0)