Особенности возврата данных из подпрограммы
Поскольку в программах командного языка допустимы процедуры, следует осветить важный вопрос - область видимости переменных.Процедуре доступны все переменные родительского блока.
Это создает определенные неудобства, особенно при использовании сторонних процедур.
Возможно при помощи команды setlocal определить в процедуре собственный уровень локализации, тогда переменные родительского контента будут недоступны. Безусловно, это более безопасный подход, но возникают сложности с возвратом значений в родительский блок.
Рассмотрим это на примерах.
:ADD :: НЕВЕРНО
setlocal enableextensions
set /a Summa=%1 + %2
set /a k=%Summa%
endlocal
exit /b 0
В данном случае значение переменной k в процедуре никак не повлияет на значение переменной с тем же именем в родительском блоке. После выполнения команды endlocal значение этой переменной теряется.
Еще один неверный пример:
:ADD :: НЕВЕРНО
setlocal enableextensions
set /a Summa=%1 + %2
endlocal
set /a k=%Summa%
exit /b 0
В момент присвоения переменной k родительского блока значение переменной SUMMA уже не существует (точнее, равна пустой строке).
Разобравшись в чем именно здесь сложности перейдем к возможным вариантам решения этой задачи.
:ADD
setlocal enableextensions enabledelayedexpansion
set /a Summa=%1 + %2
endlocal & set /a k=%Summa%
exit /b 0
Рассмотрим работу строки endlocal & set /a k=%Summa%. Командный процессор, разбирая строку, подставит значения переменных среды. Если значение переменной Summa равно, скажем, 7, то строка преобразуется в
endlocal & set /a k=7
После выполнения первой части строки - команды endlocal - значение переменной Summa уже не имеет значения, которое было внутри процедуры, но командный процессор уже подставил значение этой переменной в оператор SET !
И, после выполнения команды endlocal, переменная k уже принадлежит родительскому блоку.
Объединение операторов можно сделать не только амперсандом, но и сделав составной оператор.
:ADD
setlocal enableextensions enabledelayedexpansion
set /a Summa=%1 + %2
( endlocal
set /a k=%Summa%
exit /b 0
)
Как вариант первого решения можно использовать errorlevel для возврата одного числового значения из процедуры.
:sqr
:: Квадрат числа %1
setlocal
set /a result=%1*%1
endlocal & exit /b %result%
Обращение
call :sqr %a%
echo %a%^^2 = %errorlevel%
Описание этого метода есть здесь
Особенности использования команды CALL
Нельзя не упомянуть еще одну весьма интересную особенность команды CALL.В некоторых случаях количество вложенных переменных может превышать интеллектуальные способности интерпретатора команд. Самый простой пример - извлечение подстроки в случаях, когда начальная позиция и/или длина подстроки - переменные.
В таких случаях можно использовать недокументированную возможность команды call.
В команде call можно вызывать другую команду, например, SET.
В этом случае параметры обрабатываются иначе и становиться доступными некоторые дополнительные возможности. Часть строки сначала обрабатывается как параметр процедуры, а уже потом как часть команды SET. Проще понять это на примере.
set str=123qwerty456
set n1=3
set n2=6
Call Set d=%%str:~%n1%,%n2%%%
Echo d=%d%
Еще одна интересная возможность - сформировать в переменной полностью всю команду с параметрами, а затем отправить ее на исполнение командой call. Иногда в особо тяжелых случаях это помогает)). При использовании этого приема появляется возможность формировать такую строку по частям что иногда немаловажно.
В качестве примера приведу решение той же задачи - выделить подстроку.
Надо заметить, что в данном случае использование такого приема не дает никаких преимуществ по сравнению с предыдущим примером.
set n1=2
set n2=2
set str=asdfghjkl
...
set we=set rrr=%%str:~%n1%,%n2%%%
call %we%
echo %rrr%
Я опять опубликовал чей-то пароль в качестве значения переменной str ? )
Необходимо заметить, что без особой нужды использовать данный прием не стоит, так как это значительно увеличивает время выполнения команды и уменьшает читабельность кода.
-------------------------------------------------------------------------------------------------------
Для вопросов, обсуждений, замечаний, предложений и т. п. можете использовать раздел форума этого сайта (требуется регистрация).