1

Тема: CMD/BAT: Librarian — библиотекарь скриптов

Первоначально эту статью я начал в разделе "Разработка скриптов", чтобы иметь возможность обсудить ее идеи с сообществом, а потом, позже, можно было бы ее перенести в данный раздел, "Коллекцию". Однако, это несколько противоречит основным правилам форума - полезные идеи должны пополнять "Коллекцию". Мне предложили создать новую тему в "Коллекции", а здесь дать ссылку на ее обсуждение. Мне нравится такое решение проблемы - таким образом я не умножаю сущности (дублирование тем) и отделяю мух от котлет (обсуждение идет отдельной темой, то есть коллекционная тема не потеряется в массе возможных откликов на тему). И так. Это основное сообщение. Демонстрационный пример и обсуждение сохранены в предыдущей теме CMD/BAT Librarian - библиотекарь скриптов (обсуждение).

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

Структура
Основу "Библиотекаря" составляет скрипт libs.bat и каталог libs/, расположенный в одном каталоге с основным скриптом. В каталоге libs/, будут складываться все библиотечные файлы. Скрипт libs.bat будет собирать необходимые файлы из этого каталога и, в зависимости от аргументов, собирать временный файл для тестового исполнения или окончательный вариант. То есть он выполняет функции сборщика (linker).

1. Общий пример исполнения

libs "lib1 lib2" script.bat options

Пример выше показывает как это работает. Рассмотрим его подробно.
В каталоге libs/ имеются два файла lib1.bat и lib2.bat (кроме расширения .bat допустимо также и .cmd). Эти файлы содержат подпрограммы, необходимые для исполнения основным скриптом script.bat. "Библиотекарь" собирает временный файл из трех файлов script.bat, libs/lib1.bat, libs/lib2.bat и выполняет его, передавая остаток аргументов из options. Следует обратить внимание, что "Библиотекарь" принимает список библиотечных файлов без расширения, в то время как имя основного файла скрипта необходимо указывать полностью, с расширением.

2. Сборка, исполнение и сохранение временного файла
Имеется возможность сохранить временный файл, создаваемый в том же каталоге, где и основной файл:

libs "lib1 lib2 /k" script.bat options

Эта команда выполняется также как описано выше, но временный файл не удаляется.

3. Окончательная сборка
Если вы уверены, что скрипт готов к "промышленному" применению, то вы можете выполнить следующую команду:

libs "lib1 lib2 /c" script.bat

Аналогично, будут собраны все файлы в один, ноо результат будет сохранен под тем же именем, а оригинальный файл будет сохранен с но с расширением .bak. При этом, если такой файл уже существует, будет выдано предупреждение и файл не будет перезаписан.

4. Сборка из всех файлов
Видимо редкий случай, но он тоже реализован - собрать все файлы из библиотеки:

libs "/a" script.bat

Опции /c или /k также допустимы.

Исходный код
Первое краткое упоминание "Библиотекаря" приведено на странице CMD/BAT Librarian. Исходный актуальный код доступен по этой ссылке.

@echo off

if    "%~1" == ""   goto help
if /i "%~1" == "/h" goto help

if "%~dpnx0" == "%~dpnx2" (
    echo.Do not compile itself.
    exit /b 1
)

setlocal

:: This tool variables
set libs_path=%~dpn0
set libs_temp=%~dpn2~%~x2
set libs_back=%~dpn2.bak
set libs_make=
set libs_keep=

set libs_cmd_copy=copy /y /b
set libs_cmd_move=move /y

if not exist "%~2" (
    echo.File not found: "%~2".
    goto error
)

dir /ad "%libs_path%" >nul 2>nul
if errorlevel 1 (
    echo.Path not found: "%libs_path%".
    goto error
)

:: Collect the main script with provided libraries
%libs_cmd_copy% "%~2" "%libs_temp%" >nul

call :comment "goto :EOF">>"%libs_temp%"

for %%a in ( %~1 ) do (
    if /i "%%~a" == "/c" (
        rem Compile only, not execute
        set libs_make=1
    ) else (
    if /i "%%~a" == "/k" (
        rem Compile, execute and keep
        set libs_keep=1
    ) else (
    if /i "%%~a" == "/a" (
        rem Use all libraries
        for /f %%l in ( 'dir /b /a-d /on "%libs_path%\*.bat" "%libs_path%\*.cmd"' ) do (
            call :append "%%~l"
        )
    ) else (
    if exist "%libs_path%\%%~a.bat" (
        rem Looking for .BAT libraries
        call :append "%%~a.bat"
    ) else (
    if exist "%libs_path%\%%~a.cmd" (
        rem Looking for .CMD libraries
        call :append "%%~a.cmd"
    ) else (
        echo.Library not found: "%%~a".
        goto error
    )))))
)

:: Compile the main script and provided libraries and run by the command:
:: libs "... /C" ...
if defined libs_make (
    if exist "%libs_back%" (
        echo.File already exist: "%libs_back%".
        goto error
    )

    rem Store the original file under the backup name
    rem Save the new compiled file as the original one
    %libs_cmd_move% "%~2" "%libs_back%" && %libs_cmd_move% "%libs_temp%" "%~2"

    echo.
    echo."%~dpnx2" has been compiled with all provided libraries.
    echo.The original file has been backed up to the "%libs_back%".
    echo.

    goto EOS
)

:: Run the temporarily compiled script by the command:
:: libs "... [/K]" ...
set libs_args=

:loop
set libs_args=%libs_args% %3
shift
if not "%~3" == "" goto loop

call "%libs_temp%" %libs_args%


:EOS
if not defined libs_keep del "%libs_temp%" 2>nul
endlocal
goto :EOF


:error
if exist "%libs_temp%" del "%libs_temp%" 2>nul
endlocal
exit /b 1


:append
call :comment ":: %~n1">>"%libs_temp%"
%libs_cmd_copy% "%libs_temp%"+"%libs_path%\%~1" "%libs_temp%" >nul
goto :EOF


:comment
echo.
echo.:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
echo.:: %~n0 ^(c^) Copyright 2009 by Ildar Shaimordanov
echo.%~1
echo.
goto :EOF


:help
echo.Compiler ^(linker^) for batch files.
echo.%~n0 ^(c^) Copyright 2009 by Ildar Shaimordanov
echo.
echo.Usage:
echo.    %~n0 [/h]
echo.    %~n0 "LIBRARIES | /A [/C | /K]" PROGNAME [OPTIONS]
echo.
echo.  /A - Use all libraries
echo.  /C - Compile the main script and libraries to the whole file
echo.  /K - Keep a temporary file on a disk
goto :EOF
( 2 * b ) || ! ( 2 * b )