错误 运行 在 bat 脚本中输入参数

Error running with input a parameter in bat script

我正在尝试将一些 .doc 个文件转换为 .docx 个文件,我找到了一个解决方案:

for %F in (*.doc) do "C:\Program Files\Microsoft Office\Office12\wordconv.exe" -oice -nme "%F" "%Fx"

详细信息见:Automation: how to automate transforming .doc to .docx?

现在想用wordconv.exe的绝对路径作为入参

那我的做法是这样的:

doc2docx.bat的内容:

for %F in (*.doc) do (%1 -oice -nme "%F" "%Fx")

运行 doc2docx.bat 在控制台中:

doc2docx.bat "C:\Program Files\Microsoft Office\Office12\wordconv.exe"

我得到以下结果:

D:\book\work\temp. SPEC_NEW>D:\book\doc2docx.bat "C:\Program Files (x86)\Microsoft Office\Office12\Wordconv.exe"

此时不应有 1。

D:\book\work\temp. SPEC_NEW>for 1 -oice -nme "Fx")

D:\book\work\temp. SPEC_NEW>

消息此时不应有 1。表示'1' was unexpected at this time.

我该如何解决?

我对批处理脚本编码知之甚少。

for %%F in (*.doc) do (%1 -oice -nme "%F" "%Fx")

Batch 将 %F in (*.doc) do (% 解释为一个变量,不出所料,它没有任何值,因此它执行

for 1 -oice -nme "%F" "%Fx")

这解释了您的错误消息,因为在 for 之后不应出现 1

批次中的 metavariable(在本例中为 F)必须是 %%F。仅当 运行 直接来自提示时,是 %F.

我建议打开一个command prompt,运行 for /?,然后从第一页的顶部到最后一页的底部阅读输出帮助。第四段中已经写到,与直接在 Windows 命令提示符下使用命令 FOR 相比,在批处理文件中必须使用加倍百分号引用循环变量。

所以解决方案可能是:

for %%F in (*.doc) do %1 -oice -nme "%%F" "%%~nF.docx"

但由于以下原因,我不建议在批处理文件中使用此命令行。

原因1一章问题7:字母ADFNPSTXZadfnpstxz作为循环变量的用法中有详细解释.可以将这些字母用于循环变量,但建议不要这样做。还有许多没有特殊含义的其他 ASCII 字符,它们始终可以安全地用作循环变量。

原因2是命令FOR在当前目录中用*.doc搜索在.doc中的文件长 短文件名。因此,如果目录包含 Test1.docTest2.docxFOR 运行s 转换器 executable 具有两个文件名,因为它可以在 运行ning 命令提示符中看到 window 命令行:

for %I in (*.doc) do @echo Long name: %I - short name: %~snxI

包含 Test1.docTest2.docx 的目录的输出是:

Long name: Test1.doc - short name: TEST1.DOC
Long name: Test2.docx - short name: TEST2~1.DOC

原因 3 在 FAT32 和 exFAT 驱动器上经常出现问题,但有时甚至在 NTFS 驱动器上也会出现问题。 FOR 在每次迭代后处理与通配符模式匹配的文件时访问文件系统。在后台执行_findfirst, _findnext, _findnext, ..., _findclose。问题在于,由于 Microsoft Word 文件的转换,目录条目发生了变化,因为 *.docx 文件是在与已处理的 *.doc 文件相同的目录中创建的。所有文件系统 return 存储在其 table 中的文件名。不同之处在于 NTFS 主文件 table 是特定于区域设置的按名称排序,而 FAT32 和 exFAT 的 table 根本没有排序,因此随着每次创建或删除文件或文件夹而发生巨大变化一个目录。出于这个原因,可能会发生一些 .doc 文件被处理不止一次而其他文件被跳过,甚至可能导致无休止的 运行ning 循环。换句话说,FOR 循环行为在这种情况下是未定义的,因此循环可能偶然运行,但也可能失败。

解决方法是使用命令行:

for /F "delims=" %%I in ('%SystemRoot%\System32\where.exe *.doc 2^>nul') do %1 -oice -nme "%%I" "%%~dpnI.docx"

通过使用 I 作为循环变量可以避免问题 1。也可以使用 #BJ 以及许多其他 ASCII 字符。

FOR 选项 /F 和包含在 ' 中的一组导致在后台启动另一个命令进程 %ComSpec% /c' 中的命令行作为附加参数附加。因此它在后台执行 Windows 安装到 C:\Windows:

C:\Windows\System32\cmd.exe /c C:\Windows\System32\where.exe *.doc 2>nul

WHERE 不同于 FORDIR。它仅在长文件名中搜索扩展名为 .doc 的文件。因此,通过使用命令 WHERE 可以避免与文件扩展名 .docx 匹配的问题 2,该命令输出与通配符模式匹配的具有完整限定文件名的找到的文件(驱动器 + 路径 +名称 + 扩展名)。

阅读有关 Using command redirection operators 的 Microsoft 文档,了解 2>nul 的解释。重定向运算符 > 必须在 FOR 命令行上使用脱字符 ^ 进行转义,以便在 Windows 命令解释器处理此命令时将其解释为文字字符执行命令 FOR 之前的行,它在后台启动的单独命令进程中执行嵌入式 where 命令行。

where 为处理启动的后台命令进程的 STDOUT(标准输出)而编写的所有内容都由 cmd.exe 处理批处理文件捕获。捕获的行在开始后处理 cmd.exewhere.exe 完成后自行终止。由于这个原因,问题 3 被避免了,因为文件扩展名 .doc 的文件名列表在 运行 转换时不再改变。在开始处理之前,文件名列表已经完全加载到 cmd.exe 的内存中。

FOR with option /F 默认情况下会忽略空行,使用普通 space 和水平制表符作为字符串分隔符将每行拆分为子字符串,如果第一个 space/tab 分隔字符串以分号开头,则忽略该行,否则仅将第一个 space/tab 分隔字符串分配给指定的循环变量。这里不需要这种默认的行处理行为,因为完整的限定文件名可以包含一个或多个 space。选项字符串 "delims=" 定义了一个空的分隔符列表,这会导致完全禁用行拆分行为。 where 输出具有完整路径的文件名,因此没有捕获的行可以在开头有 ; 并且因此在这种情况下可以保留隐式默认值 eol=; 。否则使用不同的命令行导致捕获的行只是匹配通配符模式的文件的名称 eol=|eol=? 不能使用,因为 |? 都不能在文件名中使用,以避免名称以 ; 异常开头的文件被 FOR.

忽略

我建议使用以下批处理文件,它通过使用存储在 Windows 注册表中的 winword.exeexcel.exe 或 [=58] 的路径自行搜索 wordconv.exe =] 由 Application Registration 的 Office 安装程序添加,启动时没有 executable.

的文件名
@echo off
setlocal EnableExtensions DisableDelayedExpansion
goto Main

:GetConvertToolName
for %%# in (winword excel powerpnt) do (
    for /F "skip=2 tokens=1,2*" %%I in ('%SystemRoot%\System32\reg.exe query "%~1\%%#.exe" /v Path') do (
        if /I "%%I" == "Path" if exist "%%~K\wordconv.exe" for %%L in ("%%~K\wordconv.exe") do set "ConvertTool=%%~fL" & goto :EOF
    )
)
goto :EOF

:Main
set "ConvertTool="
if not "%~1" == "" if exist "%~1" if /I "%~x1" == ".exe" set "ConvertTool=%~1"
if not defined ConvertTool call :GetConvertToolName "HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths"
if not defined ConvertTool call :GetConvertToolName "HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths"
if not defined ConvertTool (
    echo ERROR: Failed to find the program wordconv.exe.
    echo/
    echo Please run %~nx0 with full name of wordconv.exe as argument.
    echo/
    pause
) else for /F "delims=" %%I in ('%SystemRoot%\System32\where.exe *.doc 2^>nul') do "%ConvertTool%" -oice -nme "%%I" "%%~dpnI.docx"
endlocal

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

  • call /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • pause /?
  • reg /?
  • reg query /?
  • set /?
  • setlocal /?
  • where /?

命令行:

for %F in (*.doc) do (%1 -oice -nme "%F" "%Fx")

应改为:

for %%F in (*.doc) do ("%~1" -oice -nme "%%F" "%%Fx")

修复语法错误(如 for /? 的帮助中所述,在批处理文件中加倍 %-符号)并正确处理引号。

但仍有改进空间:

  • 当您在系统上启用了短 8.3 文件名时,模式 *.doc 甚至可以匹配 *.docx 文件(因为名为 some long name.docx 的文件有一个短名称,例如 SOMELO~1.DOCdir 也很重视)。
  • 文件可能已经转换,因此检查是否已经存在相应的 *.docx 文件可能会有所帮助。
  • 可能没有提供参数。

以下代码涉及这些问题:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Check whether first argument points to an existing file:
if exist "%~1" (
    rem // Change into target directory:
    pushd "D:\path\to\root\directory" && (
        rem /* Instead of a standard `for` loop, use `for /F` over `dir /B` to gain the possibility
        rem    to apply an additional filter by `findstr` to avoid `*.docx` to match: */
        for /F "delims= eol=|" %%F in ('dir /B /A:-D-H-S "*.doc" ^| findstr /I "\.doc$"') do (
            rem // Check whether there is already a respective `*.docx` file:
            if not exist "%%~nF.docx" (
                rem // Attempt to convert the current `*.doc` file to `*.docx`:
                "%~1" -oice -nme "%%F" "%%Fx"
            )
        )
        rem // Return from target directory:
        popd
    )
) else (
    rem // Raise an error in case the first argument does not point to an existing file:
    >&2 echo "%~1" not found!
    exit /B 1
)

endlocal
exit /B