【问题标题】:Handling of variables in for-loop of a Windows batch file在 Windows 批处理文件的 for 循环中处理变量
【发布时间】:2014-03-27 00:06:35
【问题描述】:

我一直在用 StackOverflow 之类的线程对此进行大量研究,但虽然我觉得我已经接近解决方案,但这个问题让我很头疼。

我正在尝试做的事情:当连接特定的外部硬盘驱动器(通过 WMIC 上的 VolumeSerialNumber 区分)时,会找到驱动器号,并通过 robocopy 完成镜像。该脚本通过双击执行。这是我目前所拥有的:

FOR /F "skip=1" %%i in ('wmic logicaldisk where VolumeSerialNumber^="XXXXXXXX" get deviceid 2^>nul') DO (
    SET y=%%i
    IF [%y%]==[] GOTO hdmissing
    SET "backuphd=%%i"
    GOTO endfor
)
:endfor

robocopy "C:\Users\Herbert\Documents" "%backuphd%\Backup\Documents" /MIR

ECHO Backup done
ECHO end

:hdmissing
ECHO Couldn't find external drive

:end
PAUSE

这样,外部 HD 永远不会被检测到(%y% 始终是一个空字符串)。但是,如果我在同一个控制台会话中执行两次脚本,一切都会按预期进行。但我希望它在第一次执行时起作用。

这是我迄今为止尝试过的:

  • SET y=dummy 放在脚本的开头。始终找到 HD,触发备份到 C: 如果 HD 未实际连接(显然 SET y=%%i 不会更改 y?)
  • %y% 更改为 !y! - 始终可以再次找到 HD

【问题讨论】:

    标签: batch-file for-loop token


    【解决方案1】:

    delayed expansion 问题的第 3,576 代,因环境污染而加剧。

    没有明显的setlocal,因此y 在第一次运行后仍然设置在环境中 - 因此出现“与第一次运行不同的后期运行特征”现象。

    在块语句(a parenthesised series of statements) 中,整个块被解析并然后执行。块中的任何%var% 都将替换为该变量的值在解析块时 - 在块执行之前 - 同样适用于FOR ... DO (block)

    因此,IF (something) else (somethingelse) 将在遇到 IF 时使用 %variables% 的值执行。

    解决此问题的两种常见方法是 1) 使用 setlocal enabledelayedexpansion 并使用 !var! 代替 %var% 来访问 var 的更改值或 2) 调用子例程以使用改变了值。

    在您的情况下,关键似乎不是 setlocal enabledelayeexpansion!y! - 因为 !y! 就是这样 - 文字字符串 !y! 除非 delayedexpansionsetlocal 命令调用。

    (话虽如此,

    IF [%%i]==[] GOTO hdmissing
    

    效果一样好

    SET "y=%%i"
    IF not defined y GOTO hdmissing
    

    因为if [not] defined varvar 的运行时值进行操作。 "quoting the set arguments" 确保行上的任何杂散尾随空格不包含在分配的值中 )

    【讨论】:

    • 谢谢,我并不完全了解延迟扩展的工作原理。如果我有足够的声誉,我会支持你的答案,但我以不同的方式解决了它(见我的详细答案)。在延迟扩展下逃脱是一场噩梦。 IF [%%i]==[] GOTO hdmissing 也不好用,因为WMIC returns a <CR> at the end,这似乎不被解释为换行符,而是作为自己的字符。我认为SET y=%%i 摆脱了 IF not defined y GOTO hdmissing 在我的机器上也不起作用。
    【解决方案2】:

    正如 Magoo 已经指出的那样,!y! 不起作用,因为我忘记启用 Delayed Expansion。但是,启用它需要您转义某些字符,这对我来说似乎很烦人和乏味。可以像在this question 的两次编辑中所做的那样,只用双引号将命令括在 FOR 循环中,但我在以不同方式解决后发现。

    我所做的是移动方块

    IF [%y%]==[] GOTO hdmissing
    SET "backuphd=%%i"
    

    :endfor 之后。这样,它就在 for 循环之外,%y% 会相应地展开。

    请注意,这个解决方案之所以有效,只是因为我只需要一个项目,并且因为编程流程。

    其他有用的方法可以在this question中找到。

    但是,如果您想做同样的事情:没有 WMIC 的更简单(虽然不那么强大)的解决方案可能包括

    • 在硬盘上放一个特定名称的文件,并在备份前用IF EXIST检查它,希望用户不要删除它,并且硬盘连接到同一个驱动器号。
    • 将批处理脚本放在驱动器本身并使用相对路径名(可选:在桌面上放置一个快捷方式并再次希望驱动器始终位于同一个字母下)

    【讨论】:

      猜你喜欢
      • 2013-08-08
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多