【问题标题】:Pass path with spaces as parameter to bat file将带有空格的路径作为参数传递给 bat 文件
【发布时间】:2010-10-03 03:41:45
【问题描述】:

我有一个简单的 bat 脚本,可以将文件从已知目录复制到用户指定的目录。如何将路径(可能包含空格)传递给我的脚本并将其与 xcopy 命令一起使用?


在我的代码中,我有以下内容

:READ_PWA_PATH
    if "%1" == "" ( 
        rem Set default path
        set PWA_PATH="C:\Program Files\PWA"
        rem
        echo You have not specified your PWA url.
        echo Default will be assumed: C:\Program Files\PWA. 
        choice /C:YN /M:"Do you wish to continue [Y] or cancel the script [N]?"
            IF ERRORLEVEL ==2 GOTO CANCEL
            IF ERRORLEVEL ==1 GOTO READ_WSS_SERVER_EXTENSIONS_PATH
        GOTO END
    ) else (
        set PWA_PATH=%1
    )

如果我只是调用脚本,我会收到以下错误:

C:\Projects\Setup>install.cmd "C:\program files (x86)"

-----------------
SETUP SCRIPT
-----------------

files was unexpected at this time.
C:\Projects\Setup>

【问题讨论】:

    标签: command parameters batch-file


    【解决方案1】:

    使用"%~1"%~1 单独删除周围的引号。但是,由于您不知道输入参数%1 是否有引号,因此您应该通过"%~1" 确保它们被确定添加。这在连接变量时特别有用,例如convert.exe "%~1.input" "%~1.output"

    【讨论】:

    【解决方案2】:

    有趣的一个。我喜欢收集有关在 cmd/command 中处理引号的引号。

    您的特定脚本通过使用 %1 而不是 "%1" 得到修复!!!

    通过添加“echo on”(或去掉 echo off),您可以轻松找到它。

    【讨论】:

    • 尽管问题所有者将此答案标记为最佳,但它解决了一个问题,添加了其他问题。如果您只是从 %1 中删除引号,则您的代码可能会在未提供引号时(或根本未提供参数时)出现错误。正如@zikaS 的回答所述,最安全的解决方案是使用 %~1。
    • 如何使用 %* 转义所有参数?
    【解决方案3】:

    我认为 OP 的问题是他想要BOTH以下各项:

    • 传递可能包含空格的参数
    • 测试是否缺少参数

    正如几位发帖人所提到的,要传递包含空格的参数,您必须用双引号将实际参数值括起来。

    为了测试是否缺少某个参数,我一直学的方法是:

    if "%1" == ""
    

    但是,如果实际参数被引用(如果值包含空格,则必须如此),则变为

    if ""actual parameter value"" == ""
    

    这会导致“意外”错误。如果您改为使用

    if %1 == ""
    

    然后引用值不再发生错误。但在这种情况下,当值丢失时,测试不再起作用 -- 它变成了

    if  == ""
    

    要解决此问题,请在测试中使用任何其他字符(对 DOS 具有特殊含义的字符除外)而不是引号:

    if [%1] == []
    if .%1. == ..
    if abc%1xyz == abcxyz
    

    【讨论】:

    • 另外,使用 "%~1" 将删除外部引号并允许进行真正的测试,因为您永远不知道用户会输入什么,例如 this 或 “这个”
    【解决方案4】:

    "%~1" 大部分时间都可以使用。但是,您需要注意以下几点:

    1. Windows NT 4.0 不支持它。您需要 Windows 2000 或更高版本。 (如果您正在编写与旧操作系统兼容的脚本,请注意。)
    2. 它不会使参数安全和净化。我的观察是,无法正确清理 CMD 脚本中的命令行参数

    为了说明第二点,我举个例子:

    REM example.cmd
    ECHO %~1
    

    使用 example.cmd dummy^&DIR 运行。 & 符号在这里被转义 (^&) 以防止 shell 解释为命令分隔符,因此它成为传递给脚本的参数的一部分。 DIR 被解释为在运行脚本的子 shell 中的命令,它不应该这样做。

    引用它可能会工作一段时间,但仍然不安全:

    REM example2.cmd
    SETLOCAL EnableExtensions EnableDelayedExpansion
    SET "arg1=%~1"
    ECHO "%~1"
    ECHO !arg1:"=!
    

    example2.cmd foo^"^&DIR^&^"bar 会破坏它。 DIR 命令将运行两次,一次在 SET 之后,另一次在第一个 ECHO 之后。您会看到,您认为被正确引用的 "%~1" 被论点本身不引用。

    所以,没有办法保证解析参数的安全。

    (编辑:EnableDelayedExpansion 在 Windows NT 4 中也不起作用。感谢此处的信息:http://www.robvanderwoude.com/local.php

    【讨论】:

    • 存在a way to receive even the strangest argument。它与简单的set arg=%1 完全不同,它使用REM
    • REM 技巧很酷,但它仍然不能在所有情况下都防弹。这是命令解释器的设计(为了方便而牺牲了安全性)。 (是的,这个问题让我发疯了,因为 Unix Bourne shell 在设计上已经是防弹的!)我认为唯一的防弹方法是引入另一个延迟扩展命令行参数的SETLOCAL 选项。这样我们就可以使用类似:SETLOCAL EnableDelayedExpansionArgs & ECHO.arg1=!1
    • 你能展示一两个使用 REM 技巧失败的样本吗?
    • @jeb ,如您的链接问题的答案中所述,它不能接受换行符作为参数的一部分。我已经对此进行了实验,发现它依赖于REM 命令阻止解析shell 的元字符(&|()<>),并且CMD shell 不允许换行符作为参数的一部分。所以是的,它是防弹的,但仅当从 CMD shell 中调用脚本时。如果您从 shell 外部调用批处理脚本(例如通过 C 库中的system(3) 函数或exec(2) 系统调用),它不再是防弹的。
    • @jeb ,这就是你对付它的方法:使用 Windows PowerShell(是的,它接受换行符作为参数的一部分)myBatch.bat " foo" 如果它使用SET /P 读取什么是 REM-echoed-to-a-file,上面进入无限循环,因为 %1 的第一行是空行。
    【解决方案5】:

    如果路径包含空格,则必须用引号 (") 将其括起来。

    不确定这是否正是您要问的?

    【讨论】:

      【解决方案6】:
      @echo off
      setlocal enableextensions enabledelayedexpansion
      
      if %1=="" (     
              rem Set default path
              set PWA_PATH="C:\Program Files\PWA"
              rem
              echo You have not specified your PWA url.
              echo Default will be assumed: C:\Program Files\PWA.     
              choice /C:YN /M:"Do you wish to continue [Y] or cancel the script [N]?"
                      IF ERRORLEVEL ==2 GOTO CANCEL
                      IF ERRORLEVEL ==1 GOTO READ_WSS_SERVER_EXTENSIONS_PATH
              GOTO END
          ) else (
              set PWA_PATH=%1
              @echo !PWA_PATH! vs. %1
              goto end
          )
      :READ_WSS_SERVER_EXTENSIONS_PATH
      echo ok
      goto end
      :CANCEL
      echo cancelled
      :end
      echo. final %PWA_PATH% vs. %1
      

      正如VardhanDotNet 提到的,%1 就足够了。

      "%1%" 会在引号周围添加引号:""c:\Program Files\xxx"",这意味着:

      • '空字符串' (""),
      • 后跟“c:\Program”,
      • 后跟“意外的此处”'Files\xxx',
      • 后跟一个空字符串 ("")

      但是请注意,如果您需要在 IF 子句中使用 PWA_PATH,则需要将 if 引用为 !PWA_PATH!(因此 enabledelayedexpansion 作为脚本的开头)

      【讨论】:

        【解决方案7】:

        如果您的路径包含空格,请尝试使用%~s1。这将删除空格并将~1 附加到您的路径,更重要的是它指的是文件的绝对路径。尝试使用它。

        【讨论】:

          【解决方案8】:

          假设您想通过在 C# 代码中执行批处理文件来备份数据库。这是一个完全有效的解决方案,可以处理路径内的空格。 这适用于 Windows。不过我还没有用单声道测试过。

          C#代码:

                  public bool BackupDatabase()
              {
                  bool res = true;
                  string file = "db.bat";
                  if (!File.Exists(file)) return false;
          
                  BackupPaths.ForEach(path =>
                  {
                      Directory.CreateDirectory(path);
          
                      string filePath = Path.Combine(path, string.Format("{0}_{1}.bak", Util.ConvertDateTimeToFileName(false), DatabaseName));
                      Process process = new Process();
                      process.StartInfo.FileName = file;
                      process.StartInfo.Arguments = string.Format(" {0} {1} \\\"{2}\\\""
                          , DBServerName
                          , DatabaseName
                          , filePath);
          
                      process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
          
                      try
                      {
                          process.Start();
                          process.WaitForExit();
                      }
                      catch (Exception ee)
                      {
                          Logger.Log(ee);
                          res = false;
                      }
                  });
                  return res;
              }
          

          这里是批处理文件:

          @echo OFF
          set DB_ServerName=%1
          set Name_of_Database=%2
          set PathToBackupLocation=%3
          
          echo Server Name = '%DB_ServerName%'
          echo Name of Database = '%Name_of_Database%'
          echo Path To Backup Location = '%PathToBackupLocation%'
          
          osql -S %DB_ServerName% -E -Q "BACKUP DATABASE %Name_of_Database% TO DISK=%PathToBackupLocation%"
          

          【讨论】:

            【解决方案9】:

            在特殊情况下,您希望始终默认为当前目录,或者,只有在未传入参数时才默认为当前目录,您可以使用%CD%

            根据这个answer on Server Fault

            Windows 在伪环境变量%CD% 中维护当前目录。

            查看该帖子以获得一个很好的示例,以及如何通过命令行轻松地直接对其进行测试。

            就我而言,我需要使用命令行标志 --dir= 调用一个应用 (myapp.exe),例如:
            myapp --dir=<path-to-desired-directory>

            由于我一直希望标志始终引用我从哪个目录调用应用程序,我可以制作一个批处理文件 (mybat.bat) 来自动捕获我当前的路径,并用它打开应用程序。

            mybat . 在 mybat.bat 具有以下内容时起作用:
            myapp --dir=%1

            这很棒,但能够省略 . 更好。
            mybat 在我将内容更改为时有效:
            myapp --dir=%CD%

            【讨论】:

              猜你喜欢
              • 2014-07-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-11-24
              • 2015-02-20
              • 1970-01-01
              • 2021-12-02
              • 2023-04-08
              相关资源
              最近更新 更多