为什么手动执行的批处理脚本 "not recognized" 中的命令没问题?

Why are commands in batch script "not recognized" which are executed manually fine?

我正在编写一个批处理脚本,用于从同一文件夹中的 MSI 文件安装一些应用程序。

当我在命令提示符 window 中编写这些命令时,一切正常,所有命令都正常工作。

但是当我将它们写入批处理脚本时,突然间大多数命令如 XCOPYmsiexecDISM 会导致错误消息,如:

'XCOPY' is not recognized as an internal or external command, operable program or batch file.

谷歌搜索了一段时间后,我看到了很多关于环境变量 PATH 的评论,其中应该包含 C:\Windows\system32,我确定它包含在 PATH 中。还找到了很多关于编写完整路径的答案,我已经尝试过但没有用。

我正在开发 Windows 服务器 2012。

这是我的批处理文件的代码:

@echo off
set path=C:\                rem default path
rem get the path as parameter to the script:
set argC=0
for %%x in (%*) do Set /A argC+=1
if %argC% gtr 0  (set path=%1%)
IF %ERRORLEVEL% NEQ 0 (
    echo %me%: something went wrong with input directory
) 
echo Destenation: %path%

SETLOCAL ENABLEEXTENSIONS
SET me=%~n0
SET parent=%~dp0
echo %me%: starting installation of Python 2.7 64bit and Apache 64 bit
REM install .net 3.5
DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:installationMediaDrive:\sources\sxs
msiexec /i ".\py\python-2.7.amd64.msi" TARGETDIR=%path%/Python27 /passive /norestart ADDLOCAL=ALL
mkdir %path%\Apache24
XCOPY /e /Q ".\Apache24" %path%\Apache24

看起来批处理文件应该支持可选指定的安装目录路径作为第一个参数。用于检查此可选文件夹路径是否存在的代码非常混乱。肯定有更简单的方法来检查可选参数,如下所示。

主要问题是重新定义环境变量 PATH,这导致存储在目录 %SystemRoot\System32 和其他标准 Windows 目录中的 Windows 的标准控制台应用程序不再找到通过命令解释器 cmd.exe 执行批处理文件。

一般情况下,如果此完整文件规范字符串包含 space 字符或这些字符之一,则需要指定要执行的应用程序,并用双引号括起完整路径、文件名和文件扩展名&()[]{}^=;!'+,`~ 如 运行ning 在命令提示符 window cmd /?.

上最后输出帮助页面的最后一段中所述

但主要是为了让人类用户更容易从命令提示符 window 中手动执行应用程序和脚本,Windows 命令解释器也可以找到应用程序或脚本 运行 如果指定时没有路径且没有文件扩展名,则由其本身。

因此,如果用户只输入 xcopy 或批处理文件只包含 xcopy,Windows 命令解释器会搜索与模式 xcopy.* 匹配的文件在当前目录中首先在分号分隔的环境变量列表 PATHEXT 中定义的文件扩展名,如果没有在分号分隔的环境变量列表 PATH.[ 中的所有目录中找到下一个 suitable 文件。 =80=]

有3个环境变量PATH:

  1. 系统 PATH 存储在 Windows 注册表中:

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
    

    默认情况下,系统 PATH 中的文件夹路径用于独立于所用帐户的所有进程。

  2. 用户 PATH 存储在 Windows 注册表中:

    HKEY_LOCAL_MACHINE\Environment
    

    默认情况下,用户 PATH 中的文件夹路径仅用于使用 帐户的所有进程 运行ning已设置用户 PATH

  3. 本地PATH只保存在运行ning进程当前活动环境中的内存中。

系统用户PATH由Windows连接成一个local PATH 用于进程。

每次进程启动一个新进程,如 Windows Explorer 启动 Windows 命令解释器以执行批处理文件,复制 环境当前 运行ning 进程的 table 由 Windows 为新进程创建。因此,无论进程如何更改其自身的环境变量本地副本,都不会影响所有其他已经 运行ning 的进程。对环境变量的本地更改仅对自己的进程有效,并且由修改其变量的进程启动的所有进程。

启动批处理文件时,变量 PATHPATHEXT 的值与 运行ning 在命令提示符 window 中显示的值相同,用户帐户与在启动批处理文件时使用命令 set PATH 列出所有以 PATH 开头的变量,名称不区分大小写。

现在让我们看一下批处理文件的第二行:

set path=C:\                rem default path

这一行重新定义了localPATH环境变量。因此,环境变量 PATH 对执行批处理文件的命令进程有效,并且由该批处理文件启动的所有应用程序不再包含 C:\Windows\System32;C:\Windows;...,但现在只包含这个非常奇怪的单个文件夹路径。

C:\                rem default path

remcmd.exe的内部命令,必须单独写一行。在 C++ 中的 // 或 JavaScript 等批处理代码中不可能有行注释。在命令提示符 window rem /?.

中获取有关此命令 运行 的帮助

在 运行 宁没有安装文件夹路径作为第一个参数的批处理文件时,结果是 Windows 命令解释器搜索 dism.*msiexec.*xcopy.* 就在当前目录中,因为在驱动器 C:.

的根目录中肯定没有名称为 rem default path 且开头有很多 spaces/tabs 的目录

结论:使用path作为安装文件夹路径的变量名不是一个好主意。

批处理代码中的另一个错误是使用 %1% 指定批处理文件的第一个参数。这是错误的,因为批处理文件的参数在命令提示符 window call /? 中用 %1%2、... 运行 引用以获得帮助引用批处理文件的参数以及存在哪些可能性,例如下面使用的 %~dp0 来获取参数 0 的驱动器和路径,这是批处理文件名,即包含当前 运行ning 批处理文件的文件夹的路径.

我建议使用这个批号:

@echo off
setlocal EnableExtensions
set "SourcePath=%~dp0"
set "BatchName=%~n0"
if "%~1" == "" (
    echo %BatchName% started without an installation folder path.
    set "InstallPath=C:\"
    goto StartInstalls
)

rem Get installation folder path from first argument
rem of batch file without surrounding double quotes.
set "InstallPath=%~1"

rem Replace all forward slashes by backslashes in case of installation
rem path was passed to the batch file with wrong directory separator.
set "InstallPath=%InstallPath:/=\%"

rem Append a backslash on installation path
rem if not already ending with a backslash.
if not "%InstallPath:~-1%" == "\" set "InstallPath=%InstallPath%\"

:StartInstalls
echo %BatchName%: Installation folder: %InstallPath%

echo/
echo %BatchName%: Installing .NET 3.5 ...
DISM.exe /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:installationMediaDrive:\sources\sxs

echo/
echo %BatchName%: Installing Python 2.7 64-bit ...
%SystemRoot%\System32\msiexec.exe /i "%SourcePath%py\python-2.7.amd64.msi" TARGETDIR="%InstallPath%Python27" /passive /norestart ADDLOCAL=ALL

echo/
echo %BatchName%: Installing Apache 2.4 64-bit ...
mkdir "%InstallPath%Apache24"
%SystemRoot%\System32\xcopy.exe "%SourcePath%\Apache24" "%InstallPath%Apache24\" /C /E /H /I /K /Q /R /Y >nul

endlocal

要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。

  • call /? ... 用于解释 %~dp0%~n0%~1
  • dism /?
  • echo /?
  • endlocal /?
  • goto /?
  • if /?
  • msiexec /?
  • rem /?
  • set /?
  • setlocal /?
  • xcopy /?

同时阅读

最后看一下:

Windows 服务器的管理员应该把这里和参考页面上写的所有东西都用小指拧一下。