一般来说,最好使用if not errorlevel 1 而不是if %errorlevel% EQU 0 或if !errorlevel! EQU 0,因为这种语法确实适用于任何地方。 if not errorlevel 1 表示 IF 上一个命令/应用程序的退出代码是 NOT GREATER OR EQUAL 1 或者换句话说是 LESS THAN @987654331 @ 或 EQUAL 0 因为根据 Microsoft 的指导方针,几乎没有命令/应用程序以负值退出。这种语法在命令块内部和外部的 MS-DOS 中都可以通过在命令提示符窗口中运行时输出的命令 IF 来解释 if /?。
重定向运算符| 必须用^ 转义,仅在命令集FOR 中使用。在标准命令行上使用^| 就像在第二个reg query 命令行上使用会导致将竖线解释为文字字符,并且 REG 由于参数太多而输出错误消息。
但代码无法按预期工作的主要原因是以下行:
if %HDX_Version% GEQ 2.4
命令块内有set HDX_Version=%%a,第一行IF以(开头,最后一行以匹配)结尾,但一个非空行用字符串定义此环境变量从 Windows 注册表中读取。在运行第一个 IF 之前,在解析整个命令块时,变量引用 %HDX_Version% 被 Windows 命令处理器替换。所以很可能%HDX_Version% 被替换为空,并且执行的 IF 条件是if GEQ 2.4,这会导致由于语法错误而退出批处理文件执行。此处还需要使用延迟的环境变量扩展,即在此 IF 命令行上使用语法 !HDX_Version!。
但是,代码也不适用于 if !HDX_Version! GEQ 2.4,因为比较运算符 EQU、NEQ、GEQ 等主要用于比较两个 32 位有符号 整数强>价值观。如果左右两个参数字符串之一无法成功地将运算符转换为 32 位有符号 整数,cmd.exe 运行字符串比较并将字符串比较函数返回的整数值与值0 等于、不等于、大于等。cmd.exe 根本不支持包含. 的浮点值。有关详细信息,请参阅Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files 上的答案。
我为此任务建议以下代码:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogShare=\\[path_to_logs_share]\"
if not exist "%LogShare%" set "LogShare=%TEMP%\"
set "MinimumMajorVersion=2"
set "MinimumMinorVersion=4"
set "SoftwareKey=HKEY_LOCAL_MACHINE\SOFTWARE"
if not "%ProgramFiles(x86)%" == "" if not exist %SystemRoot%\Sysnative\cmd.exe set "SoftwareKey=%SoftwareKey%\Wow6432Node"
rem Search for Citrix receiver client.
%SystemRoot%\System32\reg.exe query %SoftwareKey%\Citrix\PluginPackages\XenAppSuite\ICA_Client >nul 2>nul
if errorlevel 1 (
echo Citrix client is not installed.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
rem Search for HDX client starting by "Citrix HDX" if receiver client was found.
for /F "tokens=8" %%I in ('%SystemRoot%\System32\reg.exe query %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall /s 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"') do set "HDX_Version=%%I" & goto EvaluateVersion
echo HDX version not found under registry key %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
:EvaluateVersion
for /F delims^=.0123456789^ eol^= %%I in ("%HDX_Version%") do (
echo Determined HDX version string "%HDX_Version%" is of unknown format.>>"%LogShare%%ComputerName%_Error.txt"
goto EndCitrixCheck
)
for /F "tokens=1,2 delims=." %%I in ("%HDX_Version%") do (
if %%I LSS %MinimumMajorVersion% (
echo Determined HDX version %HDX_Version% is too low.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
if %%I EQU %MinimumMajorVersion% (
if "%%J" == "" (
if not %MinimumMinorVersion% == 0 (
echo Determined HDX version %HDX_Version% has no minor version number.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
) else if %%J LSS %MinimumMinorVersion% (
echo Determined HDX version %HDX_Version% is too low.>>"%LogShare%%ComputerName%.txt"
goto InstallClient
)
)
echo Determined HDX version %HDX_Version% is okay.>>"%LogShare%%ComputerName%.txt"
goto EndCitrixCheck
)
echo Determined HDX version string "%HDX_Version%" is of unknown format.>>"%LogShare%%ComputerName%_Error.txt"
goto EndCitrixCheck
:InstallClient
echo Installation of HDX in progress ...>>"%LogShare%%ComputerName%.txt"
rem Add here the command lines to install the Citrix client.
:EndCitrixCheck
if "%TEMP%\" == "%LogShare%" del "%LogShare%%ComputerName%.txt"
endlocal
此代码中的第一个和最后一个 IF 条件只是为了让阅读此代码的每个人都可以运行它而不会出现错误消息。
这个批处理代码甚至可以在 Windows XP 上工作,尽管这很可能不是这个任务的要求。
根据微软文章,在访问HKEY_LOCAL_MACHINE\SOFTWARE 下的注册表项时,必须考虑 Windows x64 上的 Windows x86:
环境变量SoftwareKey首先使用标准注册表键HKEY_LOCAL_MACHINE\SOFTWARE定义。这是由cmd.exe 和reg.exe 从%SystemRoot%\SysWOW64 执行的x86 版本在32 位环境中执行32 位Windows 和批处理文件的正确键。但必须附加 \Wow6432Node 才能访问由 x64 版本的 cmd.exe 执行的批处理文件的正确键,开始 x64 版本的 reg.exe 在 64 位 Windows 上存储在 %SystemRoot%\System32 中。
重写的代码避免了在同一命令块中引用的命令块中定义/修改环境变量。因此,解决第一个主要问题的代码不需要延迟环境变量扩展。
让我们看看长长的命令行:
for /F "tokens=8" %%I in ('%SystemRoot%\System32\reg.exe query %SoftwareKey%\Microsoft\Windows\CurrentVersion\Uninstall /s 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"') do set "HDX_Version=%%I" & goto EvaluateVersion
FOR 在以cmd.exe /C 开头的单独命令进程中执行,背景中两个' 之间的圆括号中的字符串例如命令行:
C:\Windows\System32\reg.exe query HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall /s 2>nul | C:\Windows\System32\findstr.exe /I /R /C:"DisplayName.*Citrix HDX"
REG 为 x86 应用程序输出卸载注册表项的所有内容以处理 STDOUT。处理 STDERR 的错误消息输出将使用 2>nul 重定向到设备 NUL 以抑制它。 REG 在这种情况下不应输出错误消息。除了 /s 在 Windows Vista 和更高版本的 Windows 上,还可以使用 /v DisplayName 以通过 REG 仅名称为 DisplayName 的所有值输出。 REG 的输出被重定向到 | 以处理命令 FINDSTR 的 STDIN。
FINDSTR 使用正则表达式在每一行中搜索不区分大小写的字符串,从DisplayName 的任意位置开始,包含0 个或多个字符和字符串Citrix HDX。使用 /R /C:"DisplayName.*Citrix HDX" 而不仅仅是 "DisplayName.*Citrix HDX" 是必要的,否则 FINDSTR 将运行正则表达式 find 搜索 DisplayName 和 0 个或多个字符以及字符串 Citrix OR 字符串HDX 在此处不需要的行中的任何位置。 FINDSTR 希望总是输出带有感兴趣的字符串值的行,以处理单独的命令进程的 STDOUT。
阅读有关Using Command Redirection Operators 的Microsoft 文章,了解2>nul 和|。重定向运算符 > 和 | 必须在 FOR 命令行上使用插入字符 ^ 转义,以便在 Windows 命令解释器在执行命令之前处理此命令行时被解释为文字字符 FOR 在后台启动的单独命令进程中使用reg 和findstr 执行嵌入式命令行。
FOR 捕获写入已启动命令进程的 STDOUT 的输出,并逐行处理它,忽略空行,默认情况下也以分号开头的行都执行不会发生在这里。其他行使用普通空格和水平制表符作为分隔符拆分为子字符串(标记),并且由于选项tokens=8,仅将八个空格/制表符分隔的子字符串分配给指定的循环变量I。如果没有包含至少八个空格/制表符分隔的字符串的行,FOR 永远不会运行命令 SET。分配给循环变量I 的字符串按原样分配给环境变量HDX_Version,批处理文件在标签EvaluateVersion 下的行继续执行。
第二个FOR 验证分配给HDX_Version 的字符串是否仅包含一个或多个点/数字。 FOR 如果分配给HDX_Version 的字符串包含除.0123456789 以外的任何其他字符(包括字符串开头的;),则FOR 将错误消息输出到错误文件而不是标准文本文件。批处理文件的执行在批处理文件末尾继续,因为无法自动处理此错误情况。可能是由于写入此批处理文件后显示的字符串发生了变化,至少应该由批处理文件检测和报告。
否则 HDX 版本最有可能是格式 major.minor 再使用一个 FOR 将版本一分为二字符串是整数,用于使用命令 IF 的整数比较器进行评估。次要版本号不能存在,除非主要版本号等于最小主要版本号。在这种情况下,缺少的最小次要版本号被解释为0,因此还需要安装/更新 Citrix 客户端。
分配给环境变量HDX_Version 的字符串不太可能,但有可能只包含一个或多个.,在这种情况下,第三个FOR 不会执行任何命令行命令方块。这也会导致将错误消息写入错误文件并跳转到批处理文件的末尾。
要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。
echo /?
endlocal /?
goto /?
if /?
reg /?
reg query /?
rem /?
set /?
setlocal /?