ПАКЕТ ДЛЯ цикла, но начните с определенного файла

#for-loop #batch-file #cmd #windows-10 #nested-for-loop

Вопрос:

У меня есть несколько папок в Windows 10, каждая из которых содержит несколько файлов PDF. Для каждой папки мне нужно запустить GhostScript с файлами PDF папки в качестве входных данных, но с определенным файлом в качестве первого.

Каждая папка содержит файл с именем, скажем, "FirstFile-X.pdf" , где X может быть чем угодно, и для каждой папки мне нужно, чтобы этот файл был первым вводом.

У меня есть следующее в пакетном файле:

 setlocal enableDelayedExpansion
set gs="C:Program Filesgsgs9.54.0bingswin64.exe"
set options=-dNOPAUSE -q -dBATCH -sDEVICE=pdfwrite
%gs% -sDEFAULTPAPERSIZE=a4 -dBATCH
for /d %%d in (*) do (
    set a=
    set output=%%d.pdf
    for %%f in (%%d*.pdf) do (
        set "a=!a!%%d^%%~nxf "
    )
    %gs% %options% -sOutputFile=!output! !a!
)
 

Приведенный выше код работает, но он не принимает этот конкретный файл в качестве первого ввода. Возможно ли, чтобы самое сокровенное for-loop просматривалось в каждом файле в нужном мне порядке?

Ответ №1:

Ответ, данный @aschipfl, вдохновил меня на другое решение:

 @echo off
setlocal enableDelayedExpansion
set "gs=C:Program Filesgsgs9.54.0bingswin64.exe"
set "options=-dNOPAUSE -q -dBATCH -sDEVICE=pdfwrite"
"%gs%" -sDEFAULTPAPERSIZE=a4 -dBATCH
for /d %%d in (*) do (
    set a=
    for %%f in (%%d*.pdf) do (
        set string=%%~nf
        if "!string:~0,5!"=="First" (
            set "a=%%f !a!"
        ) else (
            set "a=!a!%%f "
        )
    )
    "%gs%" %options% -sOutputFile=%%d.pdf !a!
)
endlocal
 

Я просто добавляю имя файла в начало строки a , если имя файла начинается с «First», а если нет, то имя файла добавляется в конец строки a . Я также реализовал некоторые другие небольшие изменения, которые предложил @aschipfl.

Ответ №2:

Вы можете использовать дополнительный for цикл, который просто повторяет первый файл, соответствующий шаблону FirstFile-*.pdf (где ожидается только одно совпадение). Этот файл может быть исключен из другого уже существующего for цикла. Смотрите пояснительные rem комментарии в коде:

 @echo off
setlocal EnableExtensions EnableDelayedExpansion
rem // Use quoted `set` syntax to assign unquoted values but still protect special characters:
set "gs=C:Program Filesgsgs9.54.0bingswin64.exe"
set "options=-dNOPAUSE -q -dBATCH -sDEVICE=pdfwrite"
rem // Use quotation during expansion of variables:
"%gs%" -sDEFAULTPAPERSIZE=a4 -dBATCH

for /D %%d in (*) do (
    set "a="
    rem // Let an extra loop find the first file:
    for %%e in ("%%dFirstFile-*.pdf") do (
        rem /* This condition is just necessary in case more than one files are found
        rem    by the extra loop in order to avoid duplicates in the returned list: */
        if not defined a (
            rem // Append the first file:
            set "a=!a!%%~e "
            rem // Iterate over all files (including first file):
            for %%f in ("%%d*.pdf") do (
                rem // Exclude already processed first file at this point:
                if /I not "%%~NXf"=="%%~NXe" set "a=!a!%%~f "
            )
        )
    )
    rem // There is no variable `output` needed:
    "%gs%" %options% -sOutputFile=%%d !a!
)
endlocal
exit /B
 

Кроме того, я внес некоторые другие незначительные улучшения, которые также прокомментированы в коде.


Обратите внимание, что у этого кода все равно будут проблемы с путями к каталогам и файлам PDF, содержащими пробелы, и с такими, которые содержат символы ! и ^ . Чтобы преодолеть их, вам потребуется дополнительная цитата и переключение отложенного расширения:

 @echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Use quoted `set` syntax to assign unquoted values but still protect special characters:
set "gs=C:Program Filesgsgs9.54.0bingswin64.exe"
set "options=-dNOPAUSE -q -dBATCH -sDEVICE=pdfwrite"
rem // Use quotation during expansion of variables:
"%gs%" -sDEFAULTPAPERSIZE=a4 -dBATCH

for /D %%d in (*) do (
    set "a="
    set "output=%%d"
    rem // Let an extra loop find the first file:
    for %%e in ("%%dFirstFile-*.pdf") do (
        rem // Store currently iterated item:
        set "item=%%~e"
        rem /* This condition is just necessary in case more than one files are found
        rem    by the extra loop in order to avoid duplicates in the returned list: */
        if not defined a (
            rem // Toggle delayed expansion to avoid issues with `!` and `^`:
            setlocal EnableDelayedExpansion
            rem // Append the first file in a quoted manner:
            set "a=!a!"!item!" "
            rem // Transfer value `a` over `endlocal` barrier:
            for /F "delims=" %%t in ("a=!a!") do endlocal amp; set "%%t"
            rem // Iterate over all files (including first file):
            for %%f in ("%%d*.pdf") do (
                rem // Store currently iterated item:
                set "item=%%~f"
                rem // Exclude already processed first file at this point:
                if /I not "%%~NXf"=="%%~NXe" (
                    rem // Toggle delayed expansion to avoid issues with `!` and `^`:
                    setlocal EnableDelayedExpansion
                    rem // Append the current item in a quoted manner:
                    set "a=!a!"!item!" "
                    rem // Transfer value `a` over `endlocal` barrier:
                    for /F "delims=" %%t in ("a=!a!") do endlocal amp; set "%%t"
                )
            )
        )
    )
    rem // Eventually use delayed expansion as well as quotation:
    setlocal EnableDelayedExpansion
    "!gs!" !options! -sOutputFile="!output!" !a!
    endlocal
)
endlocal
exit /B
 

Комментарии:

1. Спасибо за ваш ответ. Ваш ответ вдохновил меня на то, чтобы придумать другое решение, которое не требует дополнительного вложенного цикла for. Я обновлю сообщение новым кодом.

2. На данный момент ни одна из папок или имен файлов не содержит пробелов, но я сохраню этот код, когда они это сделают 🙂

3. @smlange, я откатил редактирование вашего вопроса, чтобы удалить ваше решение. Пожалуйста, опубликуйте свой код в качестве ответа вместо этого, потому что для этого и предназначен раздел ответов! Спасибо! N. B.: Вы должны изменить if условие в своем коде if /I "!string:~0,5!"=="First" на, чтобы оно не учитывало регистр…

4. Спасибо за совет относительно if ! Я также опубликовал свое обновление в качестве ответа 🙂