简体   繁体   English

如何防止Rust的std :: process:Command将.exe的相对路径插入参数?

[英]How to prevent Rust's std::process:Command from inserting .exe's relative path into arguments?

I'm using Rust's std:process:Command to call robocopy in Windows. 我正在使用Rust的std:process:Command在Windows中调用robocopy Unfortunately, it seems that somewhere along the execution of robocopy , the relative path from the .exe is inserted before each directory. 不幸的是,似乎在执行robocopy某个地方,.exe的相对路径已插入每个目录之前。

Additionally, simple calls like net use work with the same method, though I've only tested ones that don't rely on any directories. 另外,像net use这样的简单调用也可以net use相同的方法,尽管我只测试了不依赖任何目录的调用。

Edit: updated the test folders to have spaces in their names. 编辑:更新了测试文件夹,使其名称中包含空格。

Edit 2: the code has been updated with the solution. 编辑2:代码已使用解决方案进行了更新。 The solution was to call robocopy directly and .arg() in everything individually. 解决的办法是分别直接调用robocopy和.arg() Old attempts are still commented in. 旧尝试仍在评论中。

Instead of sending 而不是发送

cmd.exe /c robocopy "C:\Test\Test 1" "C:\Test\Test 2" /MIR /copy:DAT /MT:32 /Z /R:2 /W:03 /v /LOG:"C:\Test\Test 3\logfile.log"

and it running successfully, the output has a lot of errors: 并且它成功运行,输出有很多错误:

ERROR 123 (0x0000007B) Opening Log File C:\relative\path\where\exe\is\located\"C:\Test\Test 3\logfile.log"
The filename, directory name, or volume label syntax is incorrect.

-------------------------------------------------------------------------------
   ROBOCOPY     ::     Robust File Copy for Windows
-------------------------------------------------------------------------------

   Started : Tuesday, January 8, 2019 7:35:41 AM
    Source - C:\relative\path\where\exe\is\located\"C:\Test\Test 1"\
      Dest - C:\relative\path\where\exe\is\located\"C:\Test\Test 2"\

     Files :
   Options : /V /S /E /DCOPY:DA /COPY:DAT /PURGE /MIR /Z /MT:32 /R:2 /W:3

------------------------------------------------------------------------------

ERROR : Invalid Parameter #10 : "/LOG:"C:\Test\Test 3\logfile.log""

This issue occurs using: 使用以下方法会发生此问题:

  • Rust version 1.3.1 Rust版本1.3.1

  • Windows 10 Windows 10

Code that causes this for me is shown below. 导致这种情况的代码如下所示。 To use it you'll need to put a Test folder in your C:\\ (or change it to wherever) and put inside that directory a Test 1 folder with some Testfile.txt to help show copying took place, as well as a Test 3 folder for the log to be in (not sure if the log can make its own folder - so just to be safe, premake one for it!). 要使用它,您需要在C:\\中放置一个Test文件夹(或将其更改为任何位置),然后在该目录中放置一个Test 1文件夹,其中包含一些Testfile.txt以帮助显示已进行复制以及Test 3个用于存放日志的文件夹(不确定日志是否可以创建自己的文件夹-为安全起见,请为其预先做好一个文件夹!)。 Basically you want: 基本上,您想要:

C:\Test\Test 1\Testfile.txt (name or type of file doesn't matter)
C:\Test\Test 3\

robocopy should run and make a C:\\Test\\Test 2 folder with Testfile.txt in it and put a logfile.log in C:\\Test\\Test 3 that details the transaction. robocopy应该运行,并在其中创建一个包含Testfile.txtC:\\ Test \\ Test 2文件夹,并在C:\\ Test \\ Test 3中放置一个日志文件.log ,其中详细说明了该事务。 In the end, the directories should look like this: 最后,目录应如下所示:

C:\Test\Test 1\Testfile.txt
C:\Test\Test 2\Testfile.txt
C:\Test\Test 3\logfile.log

The code follows: 代码如下:

//-----Import Built-in Libraries (not called crates)-----
use std::process::Command; //use cmd.exe

fn main()
{
    let commandOpt1 = "/MIR"; //mirror directories
    let commandOpt2 = "/copy:DAT"; //copy attributes
    let commandOpt3 = "/MT:32"; //use 32 I/O threads (low CPU still, but better bandwidth utilization)
    let commandOpt4 = "/Z"; //idr
    let commandOpt5 = "/R:2"; //Retry twice
    let commandOpt6 = "/W:03"; //Wait 3 sec between tries
    let commandOpt7 = "/v"; //verbose logging
    let commandLogStr = "/LOG:\"C:\\Test\\Test 3\\logfile.log\""; //record where the log file goes
    let commandLogNoParenthStr = "/LOG:C:\\Test\\Test 3\\logfile.log"; //record where the log file goes
    let command = format!("robocopy \"C:\\Test\\Test 1\" \"C:\\Test\\Test 2\" {} {} {} {} {} {} {} {}",
    commandOpt1,commandOpt2,commandOpt3,commandOpt4,commandOpt5,commandOpt6,
    commandOpt7,commandLogStr); //build the command
    let commandStr: &str = &*command; //these two types of strings are
    println!("TEST-Command for robocopy:{}",command);

    //let m = Command::new("cmd.exe").arg("/c").arg(commandStr).output().unwrap(); //run cmd.exe net use
    /*let m = Command::new("cmd.exe")
        .arg("/c")
        .arg("robocopy")
        .arg("\"C:\\Test\\Test 1\"")
        .arg("\"C:\\Test\\Test 2\"")
        .arg(commandOpt1)
        .arg(commandOpt2)
        .arg(commandOpt3)
        .arg(commandOpt4)
        .arg(commandOpt5)
        .arg(commandOpt6)
        .arg(commandOpt7)
        .arg(commandLogStr)
        .output().unwrap(); //run cmd.exe net use */
    let m = Command::new("robocopy")
        .arg("C:\\Test\\Test 1")
        .arg("C:\\Test\\Test 2")
        .arg(commandOpt1)
        .arg(commandOpt2)
        .arg(commandOpt3)
        .arg(commandOpt4)
        .arg(commandOpt5)
        .arg(commandOpt6)
        .arg(commandOpt7)
        .arg(commandLogNoParenthStr )
        .output().unwrap(); //run cmd.exe net use */
    let mOutput = String::from_utf8_lossy(&m.stdout); //get the output
    println!("TEST-cmd output: {}",mOutput);
    println!("TEST-cmd status: {}", m.status.success()); //reports success (true or false) of command
    println!("TEST-cmd stderr: {}", String::from_utf8_lossy(&m.stderr)); //reports error words of command
    let _ = Command::new("cmd.exe").arg("/c").arg("pause").status(); // wait for user input
}

The code gives two options, one to call robocopy in one .arg() addition and one with the .arg() s split up. 该代码提供了两种选择,一种是在.arg()附加项中调用robocopy ,另一种是将.arg()拆分为一个。 For me, they give the same result - but options are good! 对我来说,它们给出相同的结果-但是选择不错!

Also, as a note, I send everything into Command as a &str but that seems to not be required - Strings can be .arg() s too. 另外,请注意,我将所有内容都以&str发送到Command ,但这似乎不是必需的- Strings也可以是.arg()

I do not have a Windows machine nearby but I'm quite sure that the problem is in the quote characters " . 我没有Windows机器附近,但我敢肯定,这个问题是在引号字符"

Escape rules in CMD.exe are quite weird: sometimes you need quotes, sometimes you do not, and sometime you must not use them. CMD.exe中的转义规则非常怪异:有时您需要引号,有时您不需要引号,有时您一定不要使用它们。

In your case, the log file, for example is chosen with the following option (removing Rust escapes): /LOG:"C:\\Test\\Test3\\logfile.log" . 在您的情况下,例如,使用以下选项选择日志文件(删除Rust转义/LOG:"C:\\Test\\Test3\\logfile.log" ):/ /LOG:"C:\\Test\\Test3\\logfile.log" : /LOG:"C:\\Test\\Test3\\logfile.log" : /LOG:"C:\\Test\\Test3\\logfile.log" This is a file name that starts and ends with a quote. 这是一个以引号开头和结尾的文件名。 And since it does not start with a drive letter or a \\ , the OS thinks: surely it is a relative path, let's look for C:\\...\\"C:\\Test..." ! 并且由于它不是以驱动器号或\\开头,因此操作系统会认为:当然这是相对路径,让我们寻找C:\\...\\"C:\\Test..."

The solution is easy: just remove all those quote. 解决方案很简单:只需删除所有引号即可。

What I don't understand is why are you calling cmd.exe /c instead of just calling robocopy directly. 我不明白的是为什么要调用cmd.exe /c而不是直接调用robocopy Quotes in the command line are used to guide the command line parser and properly manage filenames with spaces and so on. 命令行中的引号用于指导命令行解析器,并使用空格等正确管理文件名。 But if you do Command::new() of robocopy directly, then there is no need for quoting because the arguments are already passed separatedly. 但是,如果直接执行robocopy Command::new() ,则无需引用,因为参数已经分开传递。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM