【问题标题】:How to fix waiting for a specific value of variable如何修复等待变量的特定值
【发布时间】:2022-11-25 14:58:41
【问题描述】:

我正在尝试为文件夹中的每个文件做一些工作,等到虚拟机的属性设置为特定值并执行其他一些任务:

ECHO off
setlocal enabledelayedexpansion

SET VM_NAME=Win10-Pro-x32
SET TESTS_FOLDER=C:\tests
SET STATE=
SET FINISHED_STATE=Finished

FOR %%f IN (%TESTS_FOLDER%\*) DO (
    echo "doing some task"
    :checking_loop
    IF !STATE! NEQ !FINISHED_STATE! call :check_state
    echo "doing some other task"
)

goto :eof

:check_state
    FOR /f "tokens=*" %%i IN ('VBoxManage guestproperty get %VM_NAME% "State"') DO SET STATE=%%i
    SET STATE=%STATE:~7%
    ping 127.0.0.1 -n 2 > nul
    goto :checking_loop

此代码输出 "doing some task" 并继续做其他事情(我的意思是它不会打印任何其他内容并且不会停止),如何解决?

更新:对 Mofi 的一些说明:我需要在所有迭代中等待 STATE 属性 - 此属性意味着我的脚本在来宾 VM 中完成。所以,如果我在目标文件夹中有 2 个文件,批处理文件应该这样做:

  • 恢复虚拟机
  • 将文件复制到 VM 并在 VM 中运行 python 脚本
  • 等到上面提到的 python 脚本将 STATE 属性设置为“完成”
  • 将文件从来宾复制到主机

(迭代 2)

  • 恢复虚拟机
  • 将文件复制到 VM 并在 VM 中运行 python 脚本
  • (我省略了它,但我想应该清除STATE变量)
  • 再次等待,直到上述 python 脚本将 STATE 属性设置为“完成”
  • 将文件从来宾复制到主机 我会考虑你所有的建议,但我现在的主要问题是如何将工作流从check_state 子程序返回到外部FOR 循环。 注意:我已经修复了缺失的setlocal enabledelayedexpansion 行。

更新 2:我厌倦了处理批处理,所以我用 Python 重写了我的脚本。但是,我仍然有兴趣了解如何使用批处理文件来完成它。

【问题讨论】:

  • 比较运算符NEQ主要用于整数比较,即NEQ左边的整数不等于NEQ右边的整数。出于这个原因,应该使用 IF NOT !STATE! == !FINISHED_STATE! 对区分大小写的不相等字符串进行比较。有关字符串比较的更多信息如果Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files 上查看我的回答。
  • 发布的代码不包含 setlocal EnableExtensions EnableDelayedExpansion,它根据需要为此批处理文件启用命令扩展(默认情况下启用)和延迟环境变量扩展(默认情况下禁用)。但是,问题是:checking_loopgoto :checking_loop在标签check_state下面的块中用命令调用称呼作为子例程(嵌入批处理文件中的批处理文件)。命令块中的标签以 ( 开头并以匹配的 ) 结尾是不可能的。删除行 :checking_loop 和最后一行或最好是最后两行。
  • 更改代码时不需要延迟变量扩展,如下所示:1.删除行SET FINISHED_STATE=Finished2.删除行:checking_loop3.IF NOT DEFINED STATE CALL :check_state替换IF !STATE! NEQ !FINISHED_STATE! call :check_state4.SET STATE=%STATE:~7%替换为if "%STATE:~7%" == "Finshed" goto :EOF以退出子程序,环境变量STATE定义在前七个字符上,区分大小写Finished5.最后的编辑对我来说不是很清楚,因为缺少关于什么应该是想要的行为的描述。
  • 必须将最后两行替换为 set "STATE=" & goto :EOF 才能退出子程序 on state is not Finished with environment variable STATE again undefined 并继续为了echo "doing some other task"循环或用%SystemRoot%\System32\ping.exe -n 2 127.0.0.1 >nul%SystemRoot%\System32\timeout.exe /T 1 /NOBREAK >nulgoto check_state行替换最后两行等待一秒钟并保持子程序直到状态等于Finished在这种情况下子程序永远不会又被主叫了为了环形。
  • 关于“Update2”:Mofi 已经告诉过你:不要在代码块中使用标签。你能够goto :outOfLoop,但这结束了循环上下文。 (call 不同,因为它在不中断循环的情况下返回)。当然“代码块内没有标签”也意味着“不可能跳入代码块”。在我看来,您刚刚阅读/处理了 Mofi 的第一条评论(共四条),这很遗憾,因为 Mofi 花了很多精力来解释您的代码有什么问题以及如何使其工作。不要指望在这里得到定制的“复制&粘贴&避免思考”解决方案。

标签: windows for-loop batch-file


【解决方案1】:

也许可以使用以下代码:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "VM_NAME=Win10-Pro-x32"
set "TESTS_FOLDER=C:	ests"
if exist %SystemRoot%System32	imeout.exe (
    set "WaitASecond=%SystemRoot%System32	imeout.exe /T 1 /NOBREAK"
) else (
    set "WaitASecond=%SystemRoot%System32ping.exe -n 2 -w 1000 127.0.0.1"
)

for %%I in ("%TESTS_FOLDER%*") do (
    echo doing some task
    call :wait_for_state
    if not errorlevel 1 (
        echo doing some other task
    ) else (
        echo Do something on state Finished not returned within 60 retries.
    )
)
rem Exit the batch file processing without a fall through to the subroutine.
exit /B

rem The subroutine below exits with value 0 on executed program/script has
rem output a string containing anywhere case-insensitive the word Finished.
rem The program/script is otherwise executed after a delay of one second
rem once more. But the retry loop is exited after 60 retries which is
rem approximately 60 seconds (one minute) on VBoxManage finishes always
rem quickly in a few milliseconds to avoid an endless running retry loop.
rem The value 1 is returned in this error case by the subroutine to the
rem calling FOR loop above which should handle this error case appropriately.

:wait_for_state
set "State=Wait"
set "LoopCount=60"
:Retry
for /F "tokens=*" %%J in ('VBoxManage guestproperty get "%VM_NAME%" "State"') do set "STATE=%%J"
if not "%State:Finished=%" == "%State%" exit /B 0
set /A LoopCount-=1
if %LoopCount% == 0 exit /B 1
%WaitASecond% >nul
goto Retry

该代码期望在后台执行以下命令行为了wait_for_state 子程序内部总是在几毫秒内快速完成:

C:WindowsSystem32cmd.exe /c VBoxManage guestproperty get "Win10-Pro-x32" "State"

命令为了总是等待启动的cmd.exe 的自我终止,它在执行VBoxManage(可执行文件或脚本)后自行关闭。然后为了处理写入句柄的捕获输出标准输出VBoxManage 后台命令进程的(标准输出)。

如果条件 if not "%State:Finished=%" == "%State%" exit /B 0 将分配给环境变量 State 的字符串替换为空字符串(不区分大小写)所有出现的单词 Finished(删除该单词),并将结果字符串与分配给环境变量 @ 的未修改字符串进行比较987654331@。如果分配给环境变量State的字符串确实至少包含一次不区分大小写的单词Finished,则两个比较的字符串不相等,在这种情况下,子例程以值 0 退出以继续处理批处理文件在命令行call :wait_for_state 下方。

我通过在当前目录中创建名称为VBoxManage.cmd 的第二个批处理文件来测试批处理文件,仅包含以下命令行:

@if exist test.txt del test.txt & echo ... Finished ...

当上面的批处理文件正在运行并执行时,我打开了第二个命令提示符窗口,当前目录是包含两个批处理文件的目录,不时使用命令 echo Test>Test.txt 来测试子例程的两个退出条件。

要了解使用的命令及其工作原理,请打开 command prompt 窗口,在其中执行以下命令,并仔细完整地阅读每个命令显示的帮助页面。

  • call /?
  • echo /?
  • endlocal /?
  • exit /?
  • goto /?
  • if /?
  • set /?
  • setlocal /?

【讨论】:

    猜你喜欢
    • 2019-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-13
    相关资源
    最近更新 更多