1 (изменено: Rumata, 2015-12-28 10:16:56)

Тема: CMD/BAT: Хитрости и нелепости интерпретатора

Решил собрать известные хитрости и (возможные) нелепости

* * *

Команда echo - самый безопасный и надежный вызов с использованием символа двоеточие:


echo:%VAR%

* * *

Эмуляция массивов


@echo off

setlocal enabledelayedexpansion

:: инициализировать 10 переменные вида arr.X случайными числами
for /l %%i in ( 0, 1, 9 ) do (
    set /a arr.%%i=!RANDOM!
)

set arr

endlocal

* * *

Некоторые спец. и управляющие символы


:: Backspace
for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "BS=%%A"

:: ESC
for /f %%A in ('"prompt $E&for %%B in (1) do rem"') do set "ESC=%%A"
set "ESC=%ESC:~0,1%"

:: CR
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"

:: LF
set LF=^


rem Две пустые строки выше обязательны!!!

* * *

Макросы для установки кода возврата 0 или 1 (без вызова подпрограммы)


::
:: macro.true
::
:: Returns the status (0)
::
:: Example
::     %macro.true%
::
set macro.true=( call )

::
:: macro.false
::
:: Returns the status (1)
::
:: Example
::     %macro.false%
::
set macro.false=(call)

* * *

Параметризованный вызов макросов (подробнее в теме CMD/BAT: Пакетные макросы с параметрами)


::
:: macro.call
::
:: Calls macro with parameters
::
:: Example
::     set macro.example=do ( echo %%a&echo %%b&echo %%c)
::     %macro.call% ( "arg1 arg2 arg3" ) %macro.example%
set macro.call=for /f "tokens=1-26" %%a in
( 2 * b ) || ! ( 2 * b )

2

Re: CMD/BAT: Хитрости и нелепости интерпретатора

http://www.dostips.com/forum/viewtopic. … 071#p32071
Очень интересный способ конвертации целых чисел в шестнадцатеричное представление


cmd /c exit /b 3735928559
echo %=ExitCode%
( 2 * b ) || ! ( 2 * b )

3

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Rumata пишет:

Очень интересный способ конвертации целых чисел в шестнадцатеричное представление.

Есди код не защищен авторским правом, то я непрочь переписать hex2dec заново на чистом cmd. http://forum.vingrad.ru/html/emoticons/pack/girl_crazy.gif

4

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Есди код не защищен авторским правом

Не знаю, это не ко мне. Можно спросить автора скрипта, пройдя по ссылке.

А вот это уже нелепость - пробелы влияют на результат:


for /f %A in ('"prompt $E&for %B in (1) do rem"') do @echo:%A
for /f %A in ('"prompt $E &for %B in (1) do rem"') do @echo:%A
( 2 * b ) || ! ( 2 * b )

5

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Rumata пишет:

А вот это уже нелепость - пробелы влияют на результат…

Вопрос немного спорный, но я не вижу большой нелепости в возможности использовать в команде PROMPT пробелы в непосредственном виде, из чего вытекает полностью предсказуемый результат —  он был бы таким же, если бы пробел был задан с помощью $S.
--------

А вот с псевдопеременной %CMDCMDLINE%, по крайней мере у меня, глюк — любое обращение к ней с помощью расширенной подстановки меняет её саму. Например, после команды

set var=%cmdcmdline:\=/%

все дальнейшие обращения к %CMDCMDLINE% вернут значение, присвоенное %VAR%. А после команды

rem %cmdcmdline:~2%

из %CMDCMDLINE% исчезнут первые два знака. Причём, в списке процессов можно увидеть, что командная строка меняется на самом деле.

6

Re: CMD/BAT: Хитрости и нелепости интерпретатора

wisgest
Понятно. prompt работает примерно также как и echo: в вывод попадают концевые пробелы.

А вот с псевдопеременной %CMDCMDLINE%, по крайней мере у меня, глюк — любое обращение к ней с помощью расширенной подстановки меняет её саму.

Кое-что из описанного наблюдал. А ведь должна возвращать строку вызова для данной сессии cmd.exe.

( 2 * b ) || ! ( 2 * b )

7

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Rumata пишет:

prompt работает примерно также как и echo: в вывод попадают концевые пробелы.


Кстати, для "prompt" существует недокументированная "заглушка" "$", ставящаяся в конце команды (после кодов приглашения и (или) текста). После неё допустимы не только пробелы, но и комментарии (без символов "&", "|", "<", ">" и символов, вплотную примыкающих к "$" и вместе с ним составляющих код незатребованного приглашения):

for /f %%A in ('
"prompt $E$ ЭТА ФРАЗА НЕ ПОВЛИЯЕТ НА ВЫПОЛНЕНИЕ КОМАНДЫ &for %%B in (1) do rem"
') do @echo:%%A

(приведён код для пакетного файла).



wisgest пишет:

<...> %CMDCMDLINE% <...> — любое обращение к ней с помощью расширенной подстановки меняет её саму.


Развожу руки.

8 (изменено: mljaaa, 2014-01-30 03:35:01)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Команда echo - самый безопасный и надежный вызов с использованием символа двоеточие:

echo:%VAR%

Не катит

set type=Startup Type=2
echo:%type%>>out.txt
pause
С уважением <*>

9 (изменено: Rumata, 2014-01-30 03:45:42)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

mljaaa
Все катит - проблема в "двойке" на конце строки, которая рассматривается интерпретатором как файловый декриптор стандартного потока STDERR. Проверьте следующий код:


setlocal enabledelayedexpansion
set type=Startup Type=2
echo:!type!>>out.txt
pause
endlocal
( 2 * b ) || ! ( 2 * b )

10 (изменено: mljaaa, 2014-01-30 04:06:47)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Rumata пишет:

Все катит - проблема в "двойке" на конце строки, которая рассматривается интерпретатором как файловый декриптор стандартного потока STDERR. Проверьте следующий код:


setlocal enabledelayedexpansion
set type=Startup Type=2
echo:!type!>>out.txt
pause
endlocal

Работает А все дело в "заклинании enabledelayedexpansion"
То-тоже у меня !type! так и писался как !type! :-D

С уважением <*>

11

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Ипи так:

set type=Startup Type=2
>>out.txt echo:%type%
pause

.

12

Re: CMD/BAT: Хитрости и нелепости интерпретатора

mljaaa пишет:

А все дело в "заклинании enabledelayedexpansion"

Не совсем. Проблема в том, как интепретатор работает с переменными окружения. Дело в том, что они (переменные) переменнными не являются - они макрокоманды, которые раскрываются при подстановке. В данном случае выводимая строка содержит на конце 2, которая воспринимается как дескриптор. Вот чтобы "переменные" окружения заработалиименно как переменные, используется команда setlocal enabledelayedexpansion, которая предотвращает раскрытие макросов и сохраняет содержимое нетронутым.

( 2 * b ) || ! ( 2 * b )

13

Re: CMD/BAT: Хитрости и нелепости интерпретатора

По мере возможности стараюсь отойти от использования "setlocal enabledelayedexpansion" из-за проблем с "!", особенно при построчном разборе текста в цикле "for". Поэтому ещё один вариант без "setlocal enabledelayedexpansion":

set type=Startup Type=2
call echo:%%type%%>>out.txt
pause

14

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Yury пишет:

По мере возможности стараюсь отойти от использования "setlocal enabledelayedexpansion"…

В том то и дело, что мера возможности ограничена.

set "type=Startup & Type=2"
call echo:%%type%%>>out.txt

— CALL в пролёте.

15

Re: CMD/BAT: Хитрости и нелепости интерпретатора

wisgest пишет:

В том то и дело, что мера возможности ограничена.


Полностью согласен. Для каждого отдельного случая проблема, в основном, решается:

set "type=Startup ^& Type=2"
call echo:%%type%%>>out.txt
pause

.

А вот в цикле разбора это действительно "подводные камни".

16 (изменено: Rumata, 2014-02-27 13:51:48)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Исследование не претендует на скрупулезность и серьезность. Просто сделано, как сейчас модно говорить, just for lulz. Началось с того, что хотел чуток подправить hrc-схему для batch-файлов в FarColorer. Стало интересно, какой набор символов допустим в именах меток. Как я уже намекнул, я не делал дотошного исследования и не изучил всех воможных вариантов. Поэтому, если у кого-то возникнет желание продолжить и дополнить - оригинальный скрипт в конце этого сообщения.

Здесь набор 100%-допустимых символов. Под допустимостью подразумевается, что
-- символ может находится в любом месте имени метки (в начале, в середине, в конце)
-- метка доступна при любом режиме расширения setlocal
-- метка доступна любым способом (прямое обращение goto/call :LABEL или косвенное обращение, например, goto/call :%1)

Имя метки может состоять из:

-- букв латинского алфавита
-- цифр
-- символа подчеркивания _
-- знаков препинания . (точка), - (дефис), [ и ] (квадратные скобки), { и } (фигурные скобки), / и \ (прямой и обратный слеши)
-- знаков ' и ` (прямой и обратный апострофы), ~ (тильда), @ ("собачка"), # (диез), $ (доллар)

-- ) закрывающая круглая скобка (не может рассматриваться 100%-допустимой. Подробности в обсуждении ниже)

Остальные символы либо совершенно не допустимы либо не имеют 100% поддержку.

Далее программа, для тестов. Тесты простые. Запускаете js-скрипт. Затем запускаете сгенерированный файл test_make.bat. Результат в другом файле - test_done.txt.



var chars 
    = '`123456' + '7' + '890-=' 
    + '~!@#$%^' +       '*()_+' // & is special character in the shell

    + '[]' + '\\' 
    + '{}' // | - absolutely illegal character 

    + ';\'' 
    + ':"' 

    + ',.' + '/' 
    +        '?' // < and > are redirection symbols. Let's exclude them from our tests
    ;

function tmpl(char, index)
{
/*
@echo:==== INDEX ========================================
@echo:==== direct call   : "CHAR"
call :aCHARb 1 2 3
call :aCHAR  1 2 3
call :CHARb  1 2 3
call :CHAR   1 2 3

@echo:==== direct goto   : "CHAR"
@call :L_INDEX_axb
@call :L_INDEX_ax
@call :L_INDEX_xb
@call :L_INDEX_x

@echo:==== indirect call : "CHAR"
@for %%L in ( "aCHARb" "aCHAR" "CHARb" "CHAR" ) do call :%%~L 1 2 3

@echo:==== indirect goto : "CHAR"
@call :L_INDEX "aCHARb"
@call :L_INDEX "aCHAR"
@call :L_INDEX "CHARb"
@call :L_INDEX "CHAR"

@goto :EOF

:L_INDEX
goto %~1

:L_INDEX_axb
goto :aCHARb

:L_INDEX_ax
goto :aCHAR

:L_INDEX_xb
goto :CHARb

:L_INDEX_x
goto :CHAR

:aCHARb
:aCHAR
:CHARb
:CHAR
@echo:[%1] [%*] [%0]
@goto :EOF
*/
    return arguments.callee.toString()
        .replace(/^[\s\S]+\/\*|\*\/[\s\S]+$/g, '')
        .replace(/CHAR/g, char)
        .replace(/INDEX/g, index)
        ;
};

var caller = [];
var called = [];

for (var i = 0; i < chars.length; i++) {
    var c = chars.charAt(i);
    var t = tmpl(c, i);

    caller.push('call :L' + i);

    called.push(':L' + i);
    called.push(t);
}

var fso = new ActiveXObject('Scripting.FileSystemObject');
var f = fso.OpenTextFile('test_make.bat', 2, true, 0);

f.WriteLine('@(');
f.WriteLine(caller.join('\n'));
f.WriteLine(')>test_done.txt 2>&1');
f.WriteLine('@goto :EOF');
f.WriteLine(called.join('\n'));

f.Close();
( 2 * b ) || ! ( 2 * b )

17 (изменено: wisgest, 2014-02-28 01:13:12)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Занимательное исследование!

Rumata пишет:

Здесь набор 100%-допустимых символов.

…Имя метки может состоять из:


-- знаков препинания… ) (закрывающая круглая скобка)…

Закрывающая круглая скобка требует экранирования, если используется внутри составной команды в скобках. Если это считать 100%-й поддержкой, то можно заставить работать и другие символы при должном их экранировании или удвоении: % надо удваивать в GOTO и учетверять в CALL; можно использовать чётное число кавычек, при этом между кавычками могут быть пробелы (скорее, не могут — см. след. сообщение) и специальные символы… вот забавный пример:

call :a"<"b
goto a"<"b
:a"^<"b

:: . . .

call :a" <"b
goto a" <"b
:a" <"b

— в первом случае пришлось экранировать < в самой метке, во втором случае это не обязательно, хотя и допустимо — не пойму, в чём тут дело (кажется, понял — см. след. сообшение).

Rumata пишет:

Остальные символы либо совершенно не допустимы либо не имеют 100% поддержку.

К допустимым символам следует отнести также * и ? — при проверке «indirect call» в CALL вместо меток с этими символами подставляются имена файлов, удовлетворяющие получившимся шаблонам (из-за FOR).

18

Re: CMD/BAT: Хитрости и нелепости интерпретатора

wisgest пишет:

…при этом между кавычками могут быть пробелы и специальные символы… вот забавный пример:

call :a"<"b
goto a"<"b
:a"^<"b

:: . . .

call :a" <"b
goto a" <"b
:a" <"b

— в первом случае пришлось экранировать < в самой метке, во втором случае это не обязательно, хотя и допустимо — не пойму, в чём тут дело.

Кажется, понял и не прав насчёт пробелов: во втором случае собственно метка идёт до первого пробела — остальное можно рассматривать как комментарий. В CALL и GOTO же часть аргумента после пробела тоже не влияет на выбор метки для перехода, но чётность кавычек играет роль и в %0 подпрограммы попадает :a" <"b, т.е. через %0 подпрограмме можно передать дополнительные сведения, если её метка содержит одну кавычку.

19 (изменено: Rumata, 2014-02-27 13:48:36)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Закрывающая круглая скобка требует экранирования, если используется внутри составной команды в скобках.

Вы правы. Закрывающая круглая скобка не может быть 100% поддерживаемой. Внес соответствующие измененияв свой предыдущий текст.

( 2 * b ) || ! ( 2 * b )

20 (изменено: wisgest, 2014-02-28 01:43:42)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Rumata, а что с «*» и «?»? Мне, конечно, в метках достаточно буквенно-цифровых знаков, подчёркивания и точки, но если стремится к объективности, то это, скорее, допустимые знаки, иначе они не полностью поддерживаются не только CALL, но, например, и ECHO, т.к. следующая команда их никогда не напечатает:

for %%L in ( "a*b" "a*" "*b" "*" ) do echo:%%~L

21 (изменено: Rumata, 2014-02-28 10:44:45)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

wisgest пишет:

Rumata, а что с «*» и «?»?

Но они же исключены из списка допустимых символов в метках. Именно в силу того, метка содежащая эти символы не следует третьему "правилу":  метка доступна любым способом (прямое обращение или косвенное обращение). По такому же принципу я исключил восклицательный знак - при enabledelayedexpansion кое-когда (не помню когда точно) теряется восклицательный знак.

( 2 * b ) || ! ( 2 * b )

22

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Содержимое комментария способно вызвать синтаксическую (неперехватываемую) ошибку:

set a=1
rem comment %a:=%
set a=1
:: comment %a:=%

— второй пример показывает, что содержимое комментариев созданных с помощью двоеточия подвергается синтаксическому разбору также как и REM, и даже более сильно:

:: comment ^
echo 1
echo 2
:: comment ^

echo 3
echo 4

— выведет только 2 и 4.

С помощью двоеточия комментарии можно создавать и не с начала строки:

dir &: comment

— в этом случае при включенном режиме отображения команд (ECHO ON) часть строки, содержащая комментарий, включая предшествующий амперсанд, не отображается.

23

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Содержимое комментария способно вызвать синтаксическую (неперехватываемую) ошибку

Более того, комментарии-двоеточия количеством более одного внутри циклов не допустимы - выводится сообщение вида The system cannot find the drive specified..


for /l %%l in ( 1, 1, 5 ) do @(
:: first line
:: another line
    echo:%%l
)
( 2 * b ) || ! ( 2 * b )

24 (изменено: wisgest, 2014-03-23 09:29:26)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

Rumata пишет:

Более того, комментарии-двоеточия количеством более одного внутри циклов не допустимы - выводится сообщение вида Системе не удается найти указанный диск..

Надо уточнить: не внутри циклов, а внутри составных команд, более не одного, а одного подряд.
Попробуем разобраться. Комментарии-двоеточия — строго говоря, испорченные метки.
В двух метках подряд смысла нет. Вне составных команд интерпретатор обрабатывает сценарий построчно и ничего не знает о соседних строках. Составные команды считываются целиком и можно предположить, что в строке идущей после метки или комментария-двоеточия интерпретатор не ожидает увидеть метку и в случае двух двоеточий подряд пытается истолковать команду как команду переключения рабочего диска «буква_диска:» (в связи с появлением ключа /D у команды CD многие успели позабыть о такой).

Если ограничиться одним двоеточием или разделить двоеточия между собой, например, пробелом, то в пакетных файлах ошибка исчезает, но в интерактивном режиме каждая вторая строка будет вызывать ошибку

Синтаксическая ошибка в имени файла, имени папки или метке тома.

или

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

Но всё это ошибки не приводящие к немедленному завершению скрипта и даже текущей составной команды. А вот пустая строка или закрывающая скобка после метки или комментария-двоеточия в составной команде недопустимы.

Коль зашла речь о метках внутри составных команд, то ясно, что в связи с построчной обработкой командных файлов переход на такую метку разрушит составную команду. Что при этом произойдёт с закрывающими скобками?
Если перед скобкой есть ещё какие-то непробельные символы то возможны различные случаи:

dir )

— скобка становится аргументом предыдущей команды (его частью),

dir)

— скобка становится частью команды, в большинстве случаев — несинтаксическая ошибка (файл не найден),

(dir))

— синтаксическая ошибка.

Но вот если перед скобкой нет ничего кроме пробельных символов, и последующие символы (если есть) отделены хотя бы одним пробельным (в обоих случаях к пробельным символам приравниваются запятая, точка с запятой и знак равенства), то ошибки не будет и строка не будет обрабатываться и отображаться при включенном ECHO ON —
вот ещё один способ создания комментариев (правда, только вне составных команд);
такой комментарий может быть продолжен на следующую строку с помощью «^».

На самом деле, так же как и с помощью двоеточия, комментарии с помощью закрывающей скобки возможны не с начала строки, если перед скобкой добавить «&» :

dir &) это комментарий

но такая ситуация не должна встретиться при переходе на метку внутри составной команды.

А возможны ли комментарии не до конца строки (например, для отключение части строки при отладке без переформатирования строк или для каких-то ухищрений при смешении в одном файле различных форматов)?
Да, причём при помощи штатной команды REM!
Если после REM без пробела поставить один из знаков

+ [ ] : . \ /

(по тем же соображениям, что и для ECHO «.» и «\» нежелательны,
после «/» не должны следовать
«?» + пробельные символы + дополнительные аргументы),
то следующие за ней спецсимволы будут обрабатываться как в обычной команде:

rem: многострочный^
комментарий >empty.txt & cls

(rem:comment) & dir

— пустой файл будет создан, команды «cls» и «dir» — выполнены.

Исключением является символ перенаправления в конвейер «|» — как слева, так и справа от него такая команда REM вызывает ошибку вида

"rem:" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.

_______________________________________


Rumata пишет:

Очень интересный способ конвертации целых чисел в шестнадцатеричное представление

cmd /c exit /b 3735928559
echo %=ExitCode%

Если код возврата принадлежит отрезку 32...126, то также определена переменная %=ExitCodeAscii%, содержащая символ с указанным кодом. Открыто не мной, можно найти в разных источниках и сообщается здесь лишь для полноты изложения, т.к. ограничение на принимаемые значения делает эту переменную малополезной.
%=ExitCode%, %=ExitCodeAscii% — настоящие переменные окружения в отличии, например, от %RANDOM%, их можно увидеть с помощью команды

set ""

25 (изменено: wisgest, 2014-03-23 09:39:44)

Re: CMD/BAT: Хитрости и нелепости интерпретатора

P.S.

wisgest пишет:

…последующие символы (если есть) отделены хотя бы одним пробельным…

Также закрывающая скобка без соответствующей открывающей будет действовать как начало комментария до конца строки, если непосредственно за скобкой идут открывающая скобка или символы-ограничители команды:

( & | < >
wisgest пишет:

…такой комментарий может быть продолжен на следующую строку с помощью «^».

Таким образом на следующую строку может быть продолжен комментарий, созданный с помощью неоткрытой закрывающей скобки, двоеточия  (или просто любая команда), если внутри комментария (в списке аргументов) символ кавычки встречается чётное (в том числе, нулевое) число раз, при этом, правда, экранированные  с помощью «^» кавычки не подсчитываются.