<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title><![CDATA[Серый форум &mdash; CMD/BAT: Код и данные в одном файле]]></title>
		<link>https://forum.script-coding.com/viewtopic.php?id=16605</link>
		<atom:link href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=16605&amp;type=rss" rel="self" type="application/rss+xml" />
		<description><![CDATA[Недавние сообщения в теме «CMD/BAT: Код и данные в одном файле».]]></description>
		<lastBuildDate>Wed, 22 Sep 2021 00:12:14 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[CMD/BAT: Код и данные в одном файле]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=149629#p149629</link>
			<description><![CDATA[<p>Дальше будет много текста. Желающие могут дочитать до конца, потестировать примеры и поучаствовать в критике (-:</p><h5>Вступление</h5><p>В Perl есть такая удобная вещь как <em>виртуальный файл данных</em>, обозначаемый токенами <strong>__DATA__</strong> и <strong>__END__</strong>. Эти токены используются в качестве маркеров логического окончания скрипта и начала блока данных. А весь последующий текст игнорируется интерпретатором и является своеобразным хранилищем данных, который можно прочитать с помощью специального файлового дескриптора <strong>DATA</strong>. Чуть подробнее об этом написано здесь <a href="https://perldoc.perl.org/perldata#Special-Literals">https://perldoc.perl.org/perldata#Special-Literals</a>.</p><p>Также в архивах MetaCPAN есть модуль <a href="https://metacpan.org/pod/Inline::Files">Inline::Files</a>, который расширяет стандартные возможности языка и позволяет хранить множественные виртуальные файлы.</p><p>Я решил попробовать реализовать возможности этого модуля. И кажется получилось. Несколько версий. Каждая имеет реализует (или привносит) свои цели и имеет несколько отличащийся синтаксис объявления блоков данных.</p><h5>0. Начальная версия</h5><p>Недавно я переписал один свой скрипт и воспроизвел похожий подход в пакетном скрипте. Реализацию можно посмотреть в этом файле <a href="https://github.com/ildar-shaimordanov/git-markdown-html/blob/60665dd4c857d5a4f23fdd432ef82df058a5aa26/batch/git-md-html.bat#L185-L189">batch/git-md-html.bat</a>.</p><p>Здесь фрагмент реализации, дополненный моими коментариями:</p><div class="codebox"><pre><code>
...

rem прочитать скрипт
rem пропустить все строки до строки __DATA__
rem вывести все строки ниже строки __DATA__
set &quot;DATA_FOUND=&quot;
for /f &quot;usebackq tokens=* delims=&quot; %%s in ( &quot;%~f0&quot; ) do (
	if defined DATA_FOUND echo:%%s
	if &quot;%%~s&quot; == &quot;__DATA__&quot; set DATA_FOUND=1
)

...

__DATA__
начиная с этой строки будет использован весь текст до конца файла
...
</code></pre></div><h5>1. Простота превыше безопасности</h5><p>Здесь основной упор на <em>простоту использования</em>. При этом скрипт не защищен: то есть явно перед блоком данных необходимо указать команды <strong>goto :EOF</strong> или <strong>exit /b ...</strong>, чтобы предохранить скрипт от продолжения интерпретации. Блок данных начинается со строки</p><div class="codebox"><pre><code>
&lt;начало строки&gt; __&lt;цифры, латинские буквы или подчеркивание&gt;__ &lt;конец строки&gt;
</code></pre></div><p>и продолжается до конца файла или следующей строки, соответствующей указанному синтаксису.</p><p>Короткий пример:<br /></p><div class="codebox"><pre><code>
call :extract_data DATA
...
__DATA__
...
</code></pre></div><p>Расширенный пример:<br /></p><div class="codebox"><pre><code>
@echo off

setlocal enabledelayedexpansion

call :extract_data WHO_AM_I :parse
call :extract_data DATA

goto :EOF


:parse
echo:%~1
goto :EOF


:extract_data
echo:====
if defined DATA_NAMES goto :extract_data_continue

setlocal enabledelayedexpansion
set &quot;DATA_NAMES=&quot;
for /f &quot;tokens=* delims=&quot; %%a in ( &#039;
	findstr /r &quot;^__[0-9A-Za-z_][0-9A-Za-z_]*__$&quot; &quot;%~f0&quot;
&#039; ) do set &quot;DATA_NAMES=!DATA_NAMES! %%a&quot;
endlocal &amp; set &quot;DATA_NAMES=%DATA_NAMES%&quot;

:extract_data_continue
set &quot;DATA_THIS=&quot;
for /f &quot;tokens=1,* delims=:&quot; %%a in ( &#039;
	findstr /n /r &quot;^&quot; &quot;%~f0&quot;
&#039; ) do if &quot;%%b&quot; == &quot;__%~1__&quot; (
	set &quot;DATA_THIS=%~1&quot;
) else if defined DATA_THIS (
	for %%c in ( %DATA_NAMES% ) do if &quot;%%b&quot; == &quot;%%c&quot; goto :EOF
	setlocal disabledelayedexpansion
	if &quot;%~2&quot; == &quot;&quot; (
		echo:%%b
	) else (
		call %~2 &quot;%%b&quot;
	)
	endlocal
)
goto :EOF


__DATA__
there is data block
with 2 percents %%,
2 carets: ^ and ^
and 1 exclamation mark!
You can see all of them.

__WHO_AM_I__
Hello! I am &quot;%USERNAME%&quot;
My homedir: &quot;%USERPROFILE%&quot;
</code></pre></div><h5>2. Безопасность превыше простоты</h5><p>Это усовершенствованная версия. Здесь сделан упор на <em>безопасность кода</em>: то есть блоки данных защищены и никогда не будут интерпретироваться и исполняться. Но здесь синтаксис меток данных заметно усложнился и требует большей строгости (в силу бедных возможностей самого интерпретатора и команды <strong>findstr</strong>). Блок данных начинается со строки:</p><div class="codebox"><pre><code>
&lt;начало строки&gt; goto &lt;пробел&gt; :EOF &lt;пробел&gt; &amp; &lt;пробел&gt; :&lt;цифры, латинские буквы или подчеркивание&gt; &lt;конец строки&gt;
</code></pre></div><p>и продолжается до конца файла или следующей строки, соответствующей указанному синтаксису.</p><p>Короткий пример:<br /></p><div class="codebox"><pre><code>
call :extract_data DATA
...
goto :EOF &amp; :DATA
...
</code></pre></div><p>Расширенный пример:<br /></p><div class="codebox"><pre><code>
@echo off

setlocal enabledelayedexpansion

call :extract_data WHO_AM_I :parse
call :extract_data DATA

goto :EOF


:parse
echo:%~1
goto :EOF


:extract_data
echo:====
if defined DATA_NAMES goto :extract_data_continue

setlocal enabledelayedexpansion
set &quot;DATA_NAMES=&quot;
for /f &quot;tokens=3 delims=:&quot; %%a in ( &#039;
	findstr /r /c:&quot;^goto :EOF &amp; :[0-9A-Za-z_][0-9A-Za-z_]*$&quot; &quot;%~f0&quot;
&#039; ) do set &quot;DATA_NAMES=!DATA_NAMES! %%a&quot;
endlocal &amp; set &quot;DATA_NAMES=%DATA_NAMES%&quot;

:extract_data_continue
set &quot;DATA_THIS=&quot;
for /f &quot;tokens=1,* delims=:&quot; %%a in ( &#039;
	findstr /n /r &quot;^&quot; &quot;%~f0&quot;
&#039; ) do if &quot;%%b&quot; == &quot;goto :EOF &amp; :%~1&quot; (
	set &quot;DATA_THIS=%~1&quot;
) else if defined DATA_THIS (
	for %%c in ( %DATA_NAMES% ) do if &quot;%%b&quot; == &quot;goto :EOF &amp; :%%c&quot; goto :EOF
	setlocal disabledelayedexpansion
	if &quot;%~2&quot; == &quot;&quot; (
		echo:%%b
	) else (
		call %~2 &quot;%%b&quot;
	)
	endlocal
)
goto :EOF


goto :EOF &amp; :DATA
there is data block
with 2 percents %%,
2 carets: ^ and ^
and 1 exclamation mark!
You can see all of them.

goto :EOF &amp; :WHO_AM_I
Hello! I am &quot;%USERNAME%&quot;
My homedir: &quot;%USERPROFILE%&quot;
</code></pre></div><h5>Общее для обеих версий</h5><p>Подпрограмма поиска и отображения блоков данных реализована в виде подпрограммы <strong>:extract_data</strong>, которая вызывается с поараметром - именем блока данных. Также вторым аргументом, можно указать имя команды, подпрограммы скрипта или внешней программы для пользовательской обработки данных. Однако это экспериментальная особенность и обладает рядом существенных ограничений на допустимые символы в тексте.</p><p>Файл считывается как минимум 2 раза, а точнее - количество вызовов печати блока данных + 1. Вначале файл скрипта прочитывается, чтобы найти все всех метки блоков данных. Затем файл прочитывается снова, чтобы найти и отобразить текст заданного блока.</p>]]></description>
			<author><![CDATA[null@example.com (Rumata)]]></author>
			<pubDate>Wed, 22 Sep 2021 00:12:14 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=149629#p149629</guid>
		</item>
	</channel>
</rss>
