The gs portion works fine. Thank you for that.
In my testing I saw similar errors as the OP when the filename included spaces. So I assume this question is only about batch-files and the lack of double quotes, a common problem in cmd.
When to use double quotes
The general rules I was taught are:
(See Windows NT Shell Scripting Apr 27, 1998 by Timothy Hill)
Don’t use quotes in SET statements. This matches environment variables, where values are not quoted even if there are embedded spaces.
Do use quotes anywhere environment variables are expanded, which is pretty much everywhere else: IF, FOR, CALL and command invocations.
Finally FOR variables (in a batch file) use a doubled %. Otherwise %X will resolve to just X, which is not what you want.
Note that script (and subroutine) arguments must have quotes if there are embedded spaces in the value, but are otherwise optional. Thus you cannot always wrap the parameters %0 .. %9 with quotes. Because doubled double-quotes are ignored ""a b c"" is parsed as three values a b and c not one.
The best way to normalize arguments, which may have quotes, is strip them (if present) using the ~ operator with something like:
SET SOURCE=%~1
SET DESTINATION=%~2
Then all variables will consistently not have quotes, and they should be added back everywhere else. Note %~f1 is useful for getting the full path from an argument, but is only appropriate when %1 is expected to be a filename.
Regarding the OP’s script
Values from FOR (e.g. %%F) are not wrapped with double quotes, thus must be wrapped with double quotes in case there are embedded spaces. This is what was causing the somewhat cryptic error message Error: /undefinedfilename in (xxx).
I didn't see this at first, but if you read down a bit further in the console output you will see: Last OS error: No such file or directory
In my case xxxx was the first part of a path before the embedded space.
The SET COMMANDS=
can be moved outside of the FOR loop to eliminate the need for deferred expansion. Even simpler is to eliminate the variable completely, since it is only used once.
I prefer using a subroutine to avoid the quirks of FOR loops and multiple statements in parentheses. Subroutines also eliminate the need for deferred expansion and cmd echoes each line, making expansions easy to debug.
-o %outdir%\%%~f
works (assuming no embedded spaces) but probably not for the reasons you might think. The ~ syntax takes one or more modifiers, then the argument letter from the FOR loop. Typical modifiers are: d,p,n,x. Try Call /?
for details.
In this case ‘f’ is not a modifier, since it is the last letter in the ~ expression. Instead it refers to the variable f from the FOR statement. Since there are no modifiers, %%~f is almost equivalent to %%f, except that ~ strips quotes.
I use -o "%outdir%\%~nx1"
to extract the name and extension from the 1st argument to the subroutine (%1). This handles the case where %1 is a full path. Note since ~ strips quotes they have to be added back.
-f %%~f
can also be written %%f
in your example, but it also must have quotes since values from FOR are not wrapped with quotes. Otherwise an embedded space will cause problems, as you have seen.
In my example, in a subroutine, since %1 was wrapped with quotes at the call site FOR %%F IN (*.pdf) DO Call :DoOne "%%F"
without additional quotes is correct.
The Updated Script (tested with file names that have embedded spaces)
@ECHO OFF
SETLOCAL EnableExtensions
SET OUTDIR=InvertedOutputs
SET GS=C:\Program Files\gs\gs9.22\bin\gswin64c.exe
REM Make the output directory under the current one, hiding errors (e.g. directory exists)
MKDIR %OUTDIR% 1>nul: 2>>&1
REM Iterate through pdf files in current directory only.
REM Do not recurse child folders, since this would find files in the output directory
FOR %%F IN (*.pdf) DO Call :DoOne "%%F"
GOTO :Done
REM a subroutine eliminates the need for deferred expansion. All expansions are echoed to the console.
REM e.g. Call :DoOne "%%F" - processes one file
:DoOne
REM %~nx1 = the file name + extension of the argument. Since ~ strips quotes, they added back here.
REM including the commands
REM -f %1 -- quotes are not used here, since %1 had to be quoted at the call site
ECHO ON
"%GS%" -o "%OUTDIR%\%~nx1" -sDEVICE=pdfwrite -c "{1 exch sub}{1 exch sub}{1 exch sub}{1 exch sub} setcolortransfer" -f %1
ECHO OFF
GOTO :EOF
:Done
Pause