Ошибка запуска при вводе параметра в bat-скрипт

Я пытаюсь преобразовать несколько файлов .doc в файлы .docx и нашел решение:

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

Для получения подробной информации см.: Автоматизация: как автоматизировать преобразование .doc в . документ?

Теперь я хочу использовать абсолютный путь 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\5. SPEC_NEW>D:\book\doc2docx.bat "C:\Program Files (x86)\Microsoft Office\Office12\Wordconv.exe"

此时不应有 1。

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

D:\book\work\temp\5. SPEC_NEW>

Сообщение 此时不应有 1。 означает '1' was unexpected at this time.

Как я мог это решить?

Я мало знаю о пакетном кодировании скриптов.


person gxzmail    schedule 02.12.2020    source источник


Ответы (3)


Я рекомендую открыть командную строку, запустить for /? и прочитать вывод справки сверху вниз. последней страницы. В четвертом абзаце уже написано, что в пакетном файле переменная цикла должна указываться с удвоением знака процента по сравнению с использованием команды FOR непосредственно в командной строке Windows.

Таким образом, решение может быть:

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

Но я не могу рекомендовать использовать эту командную строку в пакетном файле по следующим причинам.

Причина 1 подробно объясняется в этом ответе в главе Проблема 7: Использование букв ADFNPSTXZadfnpstxz в качестве переменной цикла. Можно использовать эти буквы для переменной цикла, но лучше этого не делать. Существует множество других символов ASCII, не имеющих специального значения, которые всегда безопасно использовать в качестве переменной цикла.

Причина 2 заключается в том, что команда FOR ищет с *.doc в текущем каталоге файлы с .doc в длинном или в коротком имени файла. Таким образом, если каталог содержит Test1.doc и Test2.docx, ЗА запускает исполняемый файл конвертера с обоими именами файлов, как это видно при запуске в окне командной строки командной строки:

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

Вывод для каталога, содержащего Test1.doc и Test2.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. Все файловые системы возвращают имена файлов, сохраненные в их таблице. Разница в том, что основная таблица файлов NTFS зависит от локали и сортируется по имени, в то время как таблица FAT32 и exFAT вообще не сортируется и поэтому резко меняется при каждом создании или удалении файла или папки в каталоге. По этой причине может случиться так, что некоторые файлы .doc обрабатываются более одного раза, а другие пропускаются, что может привести даже к бесконечному циклу выполнения. Другими словами, поведение цикла FOR в этом случае не определено, поэтому цикл может работать случайно, но также может и не работать.

Решение использует командную строку:

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

Проблемы 1 можно избежать, используя I в качестве переменной цикла. Также можно было бы использовать # или B или J и множество других символов ASCII.

FOR с параметром /F и набором, заключенным в ', приводит к запуску в фоновом режиме еще одного командного процесса с %ComSpec% /c и командной строкой в ​​', добавленными в качестве дополнительных аргументов. Поэтому он выполняется в фоновом режиме с Windows, установленной в C:\Windows:

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

WHERE отличается от FOR или DIR. Он ищет только в длинном имени файла файлы с расширением .doc. Таким образом, проблему 2 с сопоставлением файлов с расширением .docx можно избежать, используя команду WHERE, которая выводит найденные файлы, соответствующие шаблону подстановочного знака с полным именем файла (диск + путь + имя + расширение).

Прочтите документацию Microsoft о Использование операторов перенаправления команд для объяснения 2>nul. Оператор перенаправления > должен быть экранирован символом вставки ^ в командной строке FOR, чтобы он интерпретировался как буквальный символ, когда интерпретатор команд Windows обрабатывает эту командную строку перед выполнением команды FOR, которая выполняет встроенная командная строка where в отдельный командный процесс, запущенный в фоновом режиме.

Все, написанное where для обработки STDOUT (стандартный вывод) запущенного фонового командного процесса, перехватывается cmd.exe при обработке пакетного файла. Захваченные строки обрабатываются после запуска cmd.exe и завершаются после завершения where.exe. По этой причине проблема 3 исключена, поскольку список имен файлов с расширением файла .doc больше не изменяется при выполнении преобразования. Список имен файлов уже полностью загружен в память cmd.exe перед началом их обработки.

FOR с параметром /F по умолчанию приводит к игнорированию пустых строк, разбиению каждой строки на подстроки с использованием обычного пробела и горизонтальной табуляции в качестве разделителей строк, игнорированию строки, если первая строка, разделенная пробелом/табуляцией, начинается с точки с запятой , а в противном случае назначение только первой строки, разделенной пробелом/табуляцией, указанной переменной цикла. Это поведение обработки строк по умолчанию здесь нежелательно, поскольку полные имена файлов могут содержать один или несколько пробелов. Строка параметра "delims=" определяет пустой список разделителей, что приводит к полному отключению поведения разделения строк. where выводит имена файлов с полным путем, поэтому ни одна захваченная строка не может иметь ; в начале, и по этой причине в этом случае можно сохранить неявное значение по умолчанию eol=;. В противном случае при использовании другой командной строки, в результате чего захваченные строки будут просто именами файлов, соответствующими шаблону подстановки, eol=| или eol=? можно будет использовать, поскольку ни |, ни ? нельзя использовать в имени файла, чтобы избежать того, что файлы, имя которых начинается необычно с ; игнорируются FOR.

Я предлагаю использовать следующий пакетный файл, который ищет wordconv.exe, используя путь, хранящийся в реестре Windows для winword.exe или excel.exe или powerpnt.exe, добавленный установщиком Office для Регистрация приложения при запуске без имени исполняемого файла.

@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

Чтобы понять, какие команды используются и как они работают, откройте окно командной строки, выполните в нем следующие команды. , и очень внимательно прочитайте все страницы справки, отображаемые для каждой команды.

  • call /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • pause /?
  • reg /?
  • reg query /?
  • set /?
  • setlocal /?
  • where /?
person Mofi    schedule 02.12.2020

Командная строка:

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

должен прочесть:

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

исправить синтаксические ошибки (удвоенные %-символы в пакетном файле, как указано в справке for /?) и правильно обрабатывать цитаты.

Но есть еще куда расти:

  • Шаблон *.doc может даже соответствовать *.docx файлам, если в вашей системе включены короткие имена файлов 8.3 (поскольку файл с именем some long name.docx имеет короткое имя, например SOMELO~1.DOC, которое dir тоже учитывает).
  • Файлы могут быть уже преобразованы, поэтому может быть полезно проверить, существует ли уже соответствующий файл *.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
person aschipfl    schedule 02.12.2020

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

Пакет интерпретирует %F in (*.doc) do (% как переменную, которая, что неудивительно, не имеет значения, поэтому выполняется

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

Что объясняет ваше сообщение об ошибке, поскольку 1 не ожидается после for.

metavariable (в данном случае F) в партии должно быть %%F. Только если запустить прямо из подсказки, это %F.

person Magoo    schedule 02.12.2020