Есть ли способ поддерживать библиотеку пакетных сценариев?

Я пишу сложный пакетный патч-файл с генерацией других файлов. Я знаю, что эта партия не самая лучшая для этого, но у меня в основном все работает. Однако возникает головная боль, связанная с дублированием всех подпрограмм во всех файлах, если я не хочу создавать отдельный файл для каждой подпрограммы. Мой вопрос заключался в том, есть ли способ сохранить библиотечный файл из нескольких подпрограмм и как-то их вызвать?


person Vadym    schedule 18.11.2013    source источник


Ответы (2)


Итак, я собираюсь ответить на свой вопрос, потому что мне удалось заставить что-то работать.

Допустим, у вас есть файлы main.bat и lib.bat.

Основной.bat

@ECHO off
SETLOCAL
    SET return=
    SET reference=this is just a reference variable

    CALL lib.bat return "subroutine" "static arg" reference
    IF NOT "%ERRORLEVEL%"=="0" (
        ECHO Execution failed - %ERRORLEVEL%
        EXIT /b 1
    )

    ECHO.
    ECHO subroutine return value: "%return%"
    ECHO.

    CALL lib.bat NUL "procedure" "static arg" reference

    ECHO.

    CALL lib.bat return "error" "static arg" reference
    IF NOT "%ERRORLEVEL%"=="0" (
        ECHO Execution failed - %ERRORLEVEL%
        EXIT /b 1
    )
ENDLOCAL
EXIT /b 0

Lib.bat

@ECHO off

::
:: ===================================================================================
:: Library Main Handler
:: ===================================================================================
:: %~1  - [out] - NUL | reference to a return variable
:: %~2  - [in]  - subroutine label to be invoked
:: %~3+ - [in]  - optional arguments to the subroutine
::
:: ERRORLEVEL is passed through to the caller
::

SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
    SET callSub=%~2
    SET return=
    SET args=

    IF "%callSub%"=="" (
        ECHO Subroutine label was not provided to the library. 1>&2
        EXIT /b 1
    )

    :buildUpArgumentList
    IF "%~3"=="" GOTO end_buildUpArgumentList
       SET args=%args% "%~3"
    SHIFT /3
    GOTO buildUpArgumentList
    :end_buildUpArgumentList

    IF NOT "%~1"=="NUL" (
        call:%callSub% return %args%
        IF NOT "!ERRORLEVEL!"=="0" (
            EXIT /b !ERRORLEVEL!
        )
    ) ELSE (
        call:%callSub% %args%
        IF NOT "!ERRORLEVEL!"=="0" (
            EXIT /b !ERRORLEVEL!
        )
    )
(
    ENDLOCAL
    IF NOT "%~1"=="NUL" (
        SET %~1=%return%
    )
)
EXIT /b 0

::
:: ===================================================================================
:: Library Subroutine Definitions
:: ===================================================================================
::

:subroutine <r_return> <static> <r_reference>
    SETLOCAL
        ECHO subroutine^<static^>: "%~2"
        ECHO subroutine^<r_reference^>: "!%~3!"
    (
        ENDLOCAL
        SET %~1=subroutine executed OK
    )
EXIT /b 0

:procedure <static> <r_reference>
    SETLOCAL
        ECHO procedure^<static^>: "%~1"
        ECHO procedure^<r_reference^>: "!%~2!"
    ENDLOCAL
EXIT /b 0

:error <r_return> <static> <r_reference>
    SETLOCAL
        ECHO error^<static^>: "%~2"
        EXIT /b 2
        ECHO error^<r_reference^>: "!%~3!"
    (
        ENDLOCAL
        SET %~1=error executed OK
    )
EXIT /b 0

Выход:

subroutine<static>: "static arg"
subroutine<r_reference>: "this is just a reference variable"

subroutine return value: "subroutine executed OK"

procedure<static>: "static arg"
procedure<r_reference>: "this is just a reference variable"

error<static>: "static arg"
Execution failed - 2

Некоторые комментарии:

  • Вызов библиотечной подпрограммы похож на сигнатуру обычной функции: (), но без круглых скобок.
  • Вы заметите, что код библиотеки проверяет передачу NUL. В этом случае возвращаемое значение не передается подпрограмме и не возвращается обратно.
  • Аргументы подпрограммы поддерживают n-количество аргументов.
  • Библиотека поддерживает сквозную передачу на уровне ошибок.
  • Если предоставленная метка подпрограммы не существует в библиотеке, пакет вернет ERRORLEVEL=1 и сообщение «Система не может найти указанную метку пакета — [метка]».

Дополнительные мысли:

  • Я относительно новичок в пакетном написании сценариев, поэтому не все идеально.
  • Это было сделано примерно за 1 час, и я уверен, что чего-то не хватает!
  • Следует ли выполнять какое-либо экранирование при построении массива аргументов (т.е. кавычек)?

Любые другие комментарии очень ценятся!

person Vadym    schedule 18.11.2013

Более простой подход заключается в использовании следующего трюка:

  1. Заключите код, вызывающий библиотечные функции, в круглые скобки.
  2. В начале круглых скобок переименуйте текущий пакетный файл в другое имя и переименуйте файл библиотеки как текущий пакетный файл.
  3. Теперь вы можете вызывать любую библиотечную функцию так же, как и раньше.
  4. Прежде чем скобки закончатся, переименуйте файлы обратно в исходные имена.

Например:

(
rem Switch the active context to the library file:
ren "%~0" main.bat
ren libraryFile.bat "%~0"
rem From this line on you may call any function in the library file, for example:
call :FUNCTION

rem Switch the context back to original file
ren "%~0" libraryFile.bat
ren main.bat "%~0"
)

Для получения дополнительной информации см.: Как упаковать все мои функции в пакетный файл в виде отдельного файла?

person Aacini    schedule 18.11.2013
comment
Да, но это все еще трюк. Тот факт, что нужно ввести две строки, вероятность того, что они ошибутся, увеличивает риск и затрудняет отладку. Мой библиотечный подход — это просто способ хранить все функции в одном файле, что может не понравиться людям. Я вижу преимущество хранения всех функций в отдельных файлах (подход C++ против Java). Но мне нравится твой трюк! Я думаю, что есть отличные места для его применения, и я обязательно его запомню :D - person Vadym; 20.11.2013