问题
I am trying to convert a number of .doc
files into .docx
files and I found a solution:
for %F in (*.doc) do "C:\Program Files\Microsoft Office\Office12\wordconv.exe" -oice -nme "%F" "%Fx"
For the detailed info see: Automation: how to automate transforming .doc to .docx?
Now I want to use the absolute path of wordconv.exe
as an input parameter.
Then my approach is like this:
The contents of doc2docx.bat
:
for %F in (*.doc) do (%1 -oice -nme "%F" "%Fx")
Run doc2docx.bat
in console with:
doc2docx.bat "C:\Program Files\Microsoft Office\Office12\wordconv.exe"
I got the result below:
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>
The message 此时不应有 1。
means '1' was unexpected at this time.
How could I solve it?
I know little about batch script coding.
回答1:
I recommend to open a command prompt, run for /?
and read the output help from top of first to bottom of last page. There is already written in fourth paragraph that in a batch file the loop variable must be referenced with doubling the percent sign in comparison to usage of command FOR directly on Windows command prompt.
So the solution could be:
for %%F in (*.doc) do %1 -oice -nme "%%F" "%%~nF.docx"
But I can't recommend to use this command line in the batch file because of following reasons.
Reason 1 is explained in detail by this answer by the chapter Issue 7: Usage of letters ADFNPSTXZadfnpstxz as loop variable. It is possible to use these letters for a loop variable, but it is advisable not doing that. There are lots of other ASCII characters with no special meaning which are always safe to use as loop variable.
Reason 2 is that command FOR searches with *.doc
in current directory for files with .doc
in long or in short file name. So if the directory contains Test1.doc
and Test2.docx
, FOR runs the converter executable with both file names as it can be seen on running in the command prompt window the command line:
for %I in (*.doc) do @echo Long name: %I - short name: %~snxI
The output for a directory containing Test1.doc
and Test2.docx
is:
Long name: Test1.doc - short name: TEST1.DOC
Long name: Test2.docx - short name: TEST2~1.DOC
Reason 3 is often problematic on FAT32 and exFAT drives, but is sometimes even a problem on NTFS drives. FOR accesses the file system on processing the files matched by the wildcard pattern after each iteration. There is executed in background _findfirst, _findnext, _findnext, ..., _findclose. The problem is that the directory entries change because of the conversions of the Microsoft Word files as the *.docx files are created in same directory as the processed *.doc files. All file systems return the file names as stored in their table. The difference is that the NTFS master file table is locale specific sorted by name while the table of FAT32 and exFAT is not sorted at all and so changes dramatically with each creation or deletion of a file or folder in a directory. For that reason it could happen that some .doc
files are processed more than once and others are skipped and even an endless running loop could be the result. In other words the FOR loop behavior is undefined in this case and so the loop could work by chance, but could also fail.
The solution is using the command line:
for /F "delims=" %%I in ('%SystemRoot%\System32\where.exe *.doc 2^>nul') do %1 -oice -nme "%%I" "%%~dpnI.docx"
Problem 1 is avoided by using I
as loop variable. It would be also possible to use #
or B
or J
and lots of other ASCII characters.
FOR with option /F
and a set enclosed in '
results in starting in background one more command process with %ComSpec% /c
and the command line within '
appended as additional arguments. Therefore it is executed in background with Windows installed into C:\Windows
:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\where.exe *.doc 2>nul
WHERE is different to FOR or DIR. It searches only in long file name for files with extension .doc
. So problem 2 with matching also files with file extension .docx
is avoided by using command WHERE which outputs the found files matching the wildcard pattern with full qualified file name (drive + path + name + extension).
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul
. The redirection operator >
must be escaped with caret character ^
on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded where
command line in a separate command process started in background.
Everything written by where
to handle STDOUT (standard output) of started background command process is captured by cmd.exe
processing the batch file. The captured lines are processed after started cmd.exe
terminated itself after where.exe
finished. For that reason the problem 3 is avoided as the list of file names with file extension .doc
does not change anymore on running the conversion. The file names list is already completely loaded into memory of cmd.exe
before starting processing them.
FOR with option /F
results by default in ignoring empty lines, splitting up each line into substrings using normal space and horizontal tab as string delimiters, ignoring the line if the first space/tab delimited string starts with a semicolon, and otherwise assigning just first space/tab delimited string to the specified loop variable. This default line processing behavior is not wanted here because of the full qualified file names can contain one or more spaces. The option string "delims="
defines an empty list of delimiters which results in disabling the line splitting behavior completely. where
outputs the file names with full path and so no captured line can have a ;
at beginning and for that reason the implicit default eol=;
can be kept in this case. Otherwise on using a different command line resulting in captured lines being just the names of the files matching a wildcard pattern eol=|
or eol=?
could be used as neither |
nor ?
can be used in a file name to avoid that files of which name starts unusually with ;
are ignored by FOR.
I suggest to use the following batch file which searches itself for wordconv.exe
by using the path stored in Windows registry for winword.exe
or excel.exe
or powerpnt.exe
added by the installer of Office for Application Registration on being started with no file name of an 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
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
reg /?
reg query /?
set /?
setlocal /?
where /?
回答2:
The command line:
for %F in (*.doc) do (%1 -oice -nme "%F" "%Fx")
should read:
for %%F in (*.doc) do ("%~1" -oice -nme "%%F" "%%Fx")
to fixe syntax errors (doubled %
-symbols in a batch file as stated in the help of for /?
) and to properly handle quotation.
But there is still room for improvement:
- The pattern
*.doc
may even match*.docx
files when you have short 8.3 file names enabled on your system (because a file calledsome long name.docx
has got a short name likeSOMELO~1.DOC
, whichdir
regards too). - Files may already have been converted, hence a check if there already exists a respective
*.docx
file might be helpful. - There might no argument be provided.
The following code regards these issues:
@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
回答3:
for %%F in (*.doc) do (%1 -oice -nme "%F" "%Fx")
Batch is interpreting %F in (*.doc) do (%
as a variable which, unsurprisingly, has no value hence it executes
for 1 -oice -nme "%F" "%Fx")
Which explains your error message as 1
is not expected after for
.
The metavariable
(F
in this case) within a batch must be %%F
. Only if run directly from the prompt, is it %F
.
来源:https://stackoverflow.com/questions/65104844/error-running-with-input-a-parameter-in-bat-script