Библиотека функций Windows .bat/.cmd в собственном файле?
В DOS-скрипте.bat/.cmd есть хороший способ построения функций. Чтобы модулировать некоторые сценарии установки, было бы неплохо включить файл с библиотекой функций в сценарий.bat/.cmd.
я попробовал:
mainscript.bat
call library.bat
call:function1
library.bat
goto:eof
:stopCalipri -- stop alle prozesse die mit calipri zu tun haben
:: -- %~1: argument description here
SETLOCAL
REM.--function body here
set LocalVar1=dummy
set LocalVar2=dummy
echo "Called function successfully :)"
(ENDLOCAL & REM -- RETURN VALUES
IF "%~1" NEQ "" SET %~1=%LocalVar1%
IF "%~2" NEQ "" SET %~2=%LocalVar2%
)
GOTO:EOF
Когда я вызываю mainscript.bat, я получаю следующий вывод: Das Sprungziel - function1 wurde nicht gefunden.
Что означает более или менее: не удается найти точку перехода с именем function1
Есть идеи или это невозможно?
5 ответов
Это возможно, и есть несколько разных способов сделать это.
1) Скопируйте и вставьте полную "Библиотеку" в каждый из ваших файлов Works, но на самом деле это не библиотека, а ужас - изменить / исправить функцию библиотеки во всех файлах.
2) включить библиотеку через call-обертку
call batchLib.bat :length result "abcdef"
и batchLib.bat начинается с
call %*
exit /b
...
:length
...
Легко программировать, но очень медленно, так как каждый вызов библиотеки загружает пакет библиотеки и возможные проблемы с параметрами.
3) " Самозагружающаяся " библиотека BatchLibrary или как включить пакетные файлы (кэшированные)
Каждый раз он создает временный пакетный файл, объединенный из собственного кода и кода библиотеки.
Он выполняет некоторые расширенные функции при запуске библиотеки, такие как безопасный доступ к параметрам. Но на мой взгляд, это также легко использовать
Пример пользовательского скрипта
@echo off
REM 1. Prepare the BatchLibrary for the start command
call BatchLib.bat
REM 2. Start of the Batchlib, acquisition of the command line parameters, activates the code with the base-library
<:%BL.Start%
rem Importing more libraries ...
call :bl.import "bl_DateTime.bat"
call :bl.import "bl_String.bat"
rem Use library functions
call :bl.String.Length result abcdefghij
echo len=%result%
РЕДАКТИРОВАТЬ: Другой способ...
4) Макро библиотека
Вы можете использовать пакетные макросы, их легко включать и использовать.
call MacroLib.bat
set myString=abcdef
%$strLen% result,myString
echo The length of myString is %result%
Но сложно создать макрос!
Подробнее о макротехнике в пакетном "макросах" с аргументами (кэшировано)
MacroLibrary.bat
set LF=^
::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
:::: StrLen pString pResult
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
set "str=A!%%~2!"%\n%
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
Существует более простой способ загрузки библиотечных функций при каждом выполнении основного файла. Например:
@echo off
rem If current code was restarted, skip library loading part
if "%_%" == "_" goto restart
rem Copy current code and include any desired library
copy /Y %0.bat+lib1.bat+libN.bat %0.full.bat
rem Set the restart flag
set _=_
rem Restart current code
%0.full %*
:restart
rem Delete the restart flag
set _=
rem Place here the rest of the batch file
rem . . . . .
rem Always end with goto :eof, because the library functions will be loaded
rem after this code!
goto :eof
Я предложил простое решение для использования внешних библиотек с пакетными файлами, и я хотел бы попросить вас, ребята, протестировать его и найти возможные ошибки.
Как пользоваться:
- Создать библиотеку (папку с пакетными файлами библиотеки внутри)
- Поместите этот заголовок перед любым создаваемым вами пакетным файлом, который использует библиотеку.
Принцип действия:
- Создает временный файл с библиотеками, скопированными в конец файла. Он ищет библиотеки по всем путям, перечисленным в%_IncludePath%.
- Чтобы это работало, библиотеки должны быть в "функциональной" структуре. Примеры библиотек можно найти по адресу: http://www.commandline.co.uk/lib/treeview/index.php?contents.php&../treeview/main.php
Как это устроено:
- Создает временный файл с%TEMP% (не будет работать, если% TEMP% не установлен)
- Копирует себя в этот временный файл
- Ищет каждую библиотеку по этим путям:
- исходный путь к пакетному файлу
- %BatchLibraryPath%
- Любой другой путь, указанный в%_IncludePath%
- Добавляет эти библиотеки во временный файл
- Запускает временный файл
- выходит и удаляет временный файл
Преимущества:
- Пропущенные аргументы командной строки работают отлично
- Нет необходимости завершать код пользователя какой-либо специальной командой
- Библиотеки могут быть где угодно на компьютере
- Библиотеки могут находиться в общих папках с UNC-путями
- У вас могут быть библиотеки по разным путям, и все они будут добавлены (если одна и та же библиотека найдена по разным путям, используется библиотека, расположенная в самом левом пути в%_IncludePath%).
- Возвращает errorlevel, если возникает какая-либо ошибка
Код:
- Вот пример: чтобы это работало, у вас должна быть библиотека Example.bat в папке Your_batch_file.bat (или в одной из папок%_IncludePath%)
Your_batch_file.bat
@echo off & setlocal EnableExtensions
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::Your code starts in :_main
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: v1.5 - 01/08/2015 - by Cyberponk - Fixed returning to original path when using RequestAdminElevation
:: v1.4 - 25/05/2015 - by Cyberponk
:: This module includes funcions from included libraries so that you can call
:: them inside the :_main program
::
:: Options
set "_DeleteOnExit=0" &:: if 1, %_TempFile% will be deleted on exit (set to 0 if using library RequestAdminElevation)
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
(if "%BatchLibraryPath%"=="" set "BatchLibraryPath=.") &set "_ErrorCode=" &set "#include=call :_include"
set _LibPaths="%~dp0";"%BatchLibraryPath%"&set "_TempFile=%TEMP%\_%~nx0"
echo/@echo off ^& CD /D "%~dp0" ^& goto:_main> "%_TempFile%" || (echo/Unable to create "%_TempFile%" &echo/Make sure the %%TEMP%% path has Read/Write access and that a file with the same name doesn't exist already &endlocal &md; 2>nul &goto:eof ) &type "%~dpf0" >> "%_TempFile%" &echo/>>"%_TempFile%" &echo goto:eof>>"%_TempFile%" &call :_IncludeLibraries
(if "%_ErrorCode%"=="" (call "%_TempFile%" %*) else (echo/%_ErrorCode% &pause)) & (if "%_DeleteOnExit%"=="1" (del "%_TempFile%")) & endlocal & (if "%_ErrorCode%" NEQ "" (set "_ErrorCode=" & md; 2>nul)) &goto:eof
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:_include lib
set "lib=%~1.bat" &set "_included="
(if EXIST "%lib%" ( set "_included=1" &echo/>> "%_TempFile%" &type "%lib%" >> "%_TempFile%" & goto:eof )) & for %%a in (%_LibPaths%) do (if EXIST "%%~a\%lib%" ( set "_included=1" &echo/>> "%_TempFile%" &type "%%~a\%lib%" >> "%_TempFile%" &goto:endfor))
:endfor
(if NOT "%_included%"=="1" ( set "_ErrorCode=%_ErrorCode%Library '%~1.bat' not fount, aborting...&echo/Verify if the environment variable BatchLibraryPath is pointing to the right path - and has no quotes - or add a custom path to line 25&echo/" )) &goto:eof
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:_IncludeLibraries - Your included libraries go here
::::::::::::::::::::::::::::::::::::::
:: You can add custom paths to this variable:
set _LibPaths=%_LibPaths%; C:\; \\SERVER\folder
:: Add a line for each library you want to include (use quotes for paths with space)
:: Examples:
:: %#include% beep
:: %#include% Network\GetIp
:: %#include% "Files and Folders\GetDirStats"
:: %#include% "c:\Files and Folders\GetDriveSize"
:: %#include% "\\SERVER\batch\SendHello"
%#include% Example
goto:eof
::End _IncludeLibraries
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:_main - Your code goes here
::::::::::::::::::::::::::::::::::::::
echo/Example code:
call :Example "It works!"
echo/____________________
echo/Work folder: %CD%
echo/
echo/This file: %0
echo/
echo/Library paths: %_LibPaths%
echo/____________________
echo/Argument 1 = %1
echo/Argument 2 = %2
echo/All Arguments = %*
echo/____________________
pause
,
Example.bat
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:Example msg
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
setlocal ENABLEEXTENSIONS & set "msg=%1"
echo/%msg%
endlocal & goto :EOF
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Если вы хотите простой способ установить%BatchLibraryPath%, просто поместите этот файл в путь к вашей библиотеке и запустите его перед запуском Your_batch_file.bat. Этот параметр сохраняется при перезагрузке, поэтому вы запускаете его только один раз:
SetBatchLibraryPath.bat
setx BatchLibraryPath "%~dp0"
pause
Я написал сценарий для импорта подпрограмм в основной сценарий.
Синтаксис примерно такой:
if not defined _import (
rem OPTIONAL (before the "import" calls):
set "CMD_LIBRARY=<library_directory_path>"
import "[FILE_PATH1]filename1" / "DIR_PATH1"
...
import "[FILE_PATHn]filenamen" / "DIR_PATHn"
import end "%~0"
)
<MAIN SCRIPT CODE>
...
Пожалуйста, посмотрите этот ответ.
Другим решением может быть временное добавление библиотечных функций к исполняемому пакетному файлу.
Исходный файл может быть сохранен во временном файле до изменения и восстановлен после завершения. Это имеет обратную сторону: вам нужно вызвать функцию: deimport в конце, чтобы восстановить файл и удалить временный файл. Вы также должны иметь возможность записи в командный файл и в папку, в которой вы находитесь.
demo.bat
@ECHO OFF
:: internal import call or external import call via wrapper function
CALL:IMPORT test.bat "C:\path with spaces\lib 2.bat"
:: external import call
::CALL importer.bat "%~f0%" test.bat "C:\path with spaces\lib 2.bat"
CALL:TEST
CALL:LIB2TEST
CALL:DEIMPORT
GOTO:EOF
:: Internal version of the importer
:IMPORT
SETLOCAL
IF NOT EXIST "%~f0.tmp" COPY /Y "%~f0" "%~f0.tmp">NUL
SET "PARAMS=%*"
SET "PARAMS=%PARAMS:.bat =.bat+%"
SET "PARAMS=%PARAMS:.bat" =.bat"+%"
COPY /Y "%~f0"+%PARAMS% "%~f0">NUL
ENDLOCAL
GOTO:EOF
:: wrapper function for external version call
:::IMPORT
::CALL import.bat "%~f0" %*
::GOTO:EOF
:: Internal version of the deimporter
:DEIMPORT
IF EXIST "%~f0.tmp" (
COPY /Y "%~f0.tmp" "%~f0">NUL
DEL "%~f0.tmp">NUL
)
GOTO:EOF
test.bat
:test
ECHO output from test.bat
GOTO:EOF
C: \ путь с пробелами \lib 2.bat
:LIB2TEST
ECHO output from lib 2.bat
GOTO:EOF
В качестве альтернативы, используя внешнюю версию. Обратите внимание, что это импортирует функцию deimport, поэтому обязательно удалите ее в файле demo.bat.
import.bat
:: External version of the importer
SETLOCAL EnableDelayedExpansion
IF NOT EXIST "%~f1.tmp" COPY /Y "%~f1" "%~f1.tmp">NUL
SET "PARAMS=%*"
SET "PARAMS=!PARAMS:"%~f1" =!"
SET "PARAMS=%PARAMS:.bat =.bat+%"
SET "PARAMS=%PARAMS:.bat" =.bat"+%"
COPY /Y "%~f1"+%PARAMS% "%~f1">NUL
:: external version of the importer - remove the internal one before use!
ECHO :DEIMPORT>>"%~f1"
ECHO IF EXIST ^"%%~f0.tmp^" ^(>>"%~f1"
ECHO. COPY /Y ^"%%~f0.tmp^" ^"%%~f0^"^>NUL>>"%~f1"
ECHO. DEL ^"%%~f0.tmp^"^>NUL>>"%~f1"
ECHO ^)>>"%~f1"
ECHO GOTO:EOF>>"%~f1"
ENDLOCAL
GOTO:EOF
Хорошо... быстро и грязно, потому что я парень из UNIX... создай свой "библиотечный" файл
1 @ECHO OFF<br>
2 SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION & PUSHD<br>
3 :: -----------------------------------------------<br>
4 :: $Id$<br>
5 :: <br>
6 :: NAME:<br>
7 :: PURPOSE:<br>
8 :: NOTES:<br>
9 :: <br>
10 :: INCLUDES --------------------------------- --<br>
11 :: DEFINES --------------------------------- --<br>
12 :: VARIABLES --------------------------------- --<br>
13 :: MACROS --------------------------------- --<br>
14 <br>
15 GOTO :MAINLINE<br>
16 <br>
17 :: FUNCTIONS --------------------------------- --<br>
18 <br>
19 :HEADER<br>
20 ECHO ^<HTML^><br>
21 ECHO ^<HEAD^><br>
22 ECHO ^<TITLE^>%1^</TITLE^><br>
23 ECHO ^</HEAD^><br>
24 ECHO ^<BODY^><br>
25 GOTO :EOF<br>
26 <br>
27 :TRAILER<br>
28 ECHO ^</BODY^><br>
29 ECHO ^</HTML^><br>
30 GOTO :EOF<br>
31 <br>
32 :: MAINLINE --------------------------------- --<br>
33 :MAINLINE<br>
34 <br>
35 IF /I "%1" == "HEADER" CALL :HEADER %2<br>
36 IF /I "%1" == "TRAILER" CALL :TRAILER<br>
37 <br>
38 ENDLOCAL & POPD<br>
39 :: HISTORY ------------------------------------<br>
40 :: $Log$<br>
41 :: END OF FILE --------------------------------- --<br>
это должно быть довольно простым для вас... в строке 15 мы делаем переход к главной линии, чтобы начать фактическое выполнение. В строках 19 и 27 мы создаем точки входа для наших процедур. В строках 35 и 36 - вызовы внутренних процедур.
Теперь вы создаете файл, который будет вызывать подпрограммы библиотеки..
1 @ECHO OFF<br>
2 SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION & PUSHD<br>
3 :: -----------------------------------------------<br>
4 :: $Id$<br>
5 :: <br>
6 :: NAME:<br>
7 :: PURPOSE:<br>
8 :: NOTES:<br>
9 :: <br>
10 :: INCLUDES --------------------------------- --<br>
11 <br>
12 SET _LIB_=PATH\TO\LIBRARIES\LIBNAME.BAT<br>
13 <br>
14 :: DEFINES --------------------------------- --<br>
15 :: VARIABLES --------------------------------- --<br>
16 :: MACROS --------------------------------- --<br>
17 <br>
18 GOTO :MAINLINE<br>
19 <br>
20 :: FUNCTIONS --------------------------------- --<br>
21 :: MAINLINE --------------------------------- --<br>
22 :MAINLINE<br>
23 <br>
24 call %_LIB_% header foo<br>
25 call %_LIB_% trailer<br>
26 <br>
27 ENDLOCAL & POPD<br>
28 :: HISTORY ------------------------------------<br>
29 :: $Log$<br>
30 :: END OF FILE --------------------------------- --<br>
<br>
строка 12 "импортирует" "библиотеку"... на самом деле это просто синтаксический сахар, который облегчает последующие вызовы...