Я пишу сложный пакетный патч-файл с генерацией других файлов. Я знаю, что эта партия не самая лучшая для этого, но у меня в основном все работает. Однако возникает головная боль, связанная с дублированием всех подпрограмм во всех файлах, если я не хочу создавать отдельный файл для каждой подпрограммы. Мой вопрос заключался в том, есть ли способ сохранить библиотечный файл из нескольких подпрограмм и как-то их вызвать?
Есть ли способ поддерживать библиотеку пакетных сценариев?
Ответы (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
Более простой подход заключается в использовании следующего трюка:
- Заключите код, вызывающий библиотечные функции, в круглые скобки.
- В начале круглых скобок переименуйте текущий пакетный файл в другое имя и переименуйте файл библиотеки как текущий пакетный файл.
- Теперь вы можете вызывать любую библиотечную функцию так же, как и раньше.
- Прежде чем скобки закончатся, переименуйте файлы обратно в исходные имена.
Например:
(
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
Да, но это все еще трюк. Тот факт, что нужно ввести две строки, вероятность того, что они ошибутся, увеличивает риск и затрудняет отладку. Мой библиотечный подход — это просто способ хранить все функции в одном файле, что может не понравиться людям. Я вижу преимущество хранения всех функций в отдельных файлах (подход C++ против Java). Но мне нравится твой трюк! Я думаю, что есть отличные места для его применения, и я обязательно его запомню :D
- person Vadym; 20.11.2013