Страница 1 из 1

Командная строка. Переменные внутри группы команд

Добавлено: 06 мар 2017 17:16, Пн
UncleFather

Проблема:

При использовании переменных окружения в командных файлах внутри группы команд, задаваемой скобками, например в командах IF или FOR, присваиваемое значение остается без изменения при его модификации.

Другими словами, обращаясь к переменной внутри группы команд IF так, как мы это обычно делаем (то есть заключая переменную с двух сторон в знаки процента), мы не получим ожидаемый результат. Ситуация похожа на то, что оператор SET не работает.

Код: Выделить всё

@ECHO OFF
set VAR=before
if "%VAR%" == "before" (
set VAR=after
@echo Со знаком процента=%VAR%
)

В этом примере мы получим текстовую строку Со знаком процента=before, вместо ожидаемой Со знаком процента=after


Объяснение:

При использовании переменных окружения в командных файлах существует определенное ограничение, связанное с тем фактом, что присваиваемое значение остается без изменения при его модификации внутри группы команд, задаваемой скобками, например в командах IF или FOR . Для обхода данного ограничения используется запуск командного процессора с параметром /V:ON и вместо знаков процента, для получения принимаемого переменной значения, используются восклицательные знаки. Кроме того, существует возможность использовать стандартный запуск командного процессора, но с локальным включением данного режима командой setlocal enabledelayedexpansion:

Код: Выделить всё

setlocal enableextensions enabledelayedexpansion

Разница в результатах использования значений переменных довольно наглядно демонстрируется следующим командным файлом:

Код: Выделить всё

Setlocal EnableDelayedExpansion
@ECHO OFF
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" @echo Со знаком процента=%VAR% , Со знаком вопроса=!VAR!
)

Команда set VAR=after выполняется внутри подпрограммы, ограниченной скобками и, если убрать команду Setlocal EnableDelayedExpansion или не использовать для получения значения переменной VAR восклицательные знаки, ее значение останется старым (тем, что было установлено до входа в подпрограмму). Аналогичная же проблема наблюдается и тогда, когда значение переменной изменяется внутри цикла команды FOR. Например, для получения списка файлов текущего каталога такой командный файл не будет работать:

Код: Выделить всё

set LIST=
for %%i in (*) do set LIST=%LIST% %%i
echo %LIST%

Значение переменной LIST внутри цикла изменено не будет. Для того, чтобы это произошло, командный файл нужно изменить следующим образом:

Код: Выделить всё

Setlocal EnableDelayedExpansion
set LIST=
for %%i in (*) do set LIST=!LIST! %%i
echo %LIST%

Теперь, значение переменной LIST внутри цикла FOR будет изменяться, последовательно принимая значения имен файлов, разделенных пробелом (set LIST=!LIST! %%i)


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

  1. включить режим расширения переменной среды командой setlocal enabledelayedexpansion

  2. Обращаться к переменной, ставя её имя не в знаки процента, как обычно, а в восклицательные знаки.



Примеры решения:

Приведу пример командного файла, запускающего дефолтный просмотровщик с картинками из заданного расположения через случайные промежутки времени для доменных пользователей, членов группы Feemale.

Код: Выделить всё

REM Выключаем вывод команд на экран
@echo off
REM Включаем расширения командного процессора и расширения переменной среды
setlocal enableextensions enabledelayedexpansion

REM Инициализируем переменную IsMemeber, хранящую признак членства пользователя в доменной группе
set IsMemeber=Not Memeber
REM Выполняем поиск вхождения "Feemale" среди доменных групп пользователя
net user %Username% /DOMAIN | find /i "Feemale"
REM Если вхождение находится, присваиваем переменной IsMemeber значение Memeber
if %errorlevel% == 0 set IsMemeber=Memeber

REM Проверяем значение переменной IsMemeber и, если оно равно Memeber, выполняем набор команд в скобках
if %IsMemeber% == Memeber (
REM Присваиваем переменной TimeToStart случайное значение в диапазоне от 30 до 630
SET /A TimeToStart=%RANDOM%*600/32767+30
REM Делаем задержку командой Ping, равную сгенерированному значению переменной TimeToStart
PING -n 1 -w !TimeToStart!000 192.168.11.33 > nul
REM Открываем заданную картинку 01.jpg дефолтным просмотровщиком из сетевой папки \\Server\Share\Greetings\!2017\!!NY\20170308
REM Чтобы экранировать восклицательные знаки в пути, применяем символ экранирования ^
REM Чтобы не ждать окончания выполнения команды (то есть не дожидаться закрытия просмотровщика), используем команду Start
start "Greeting 01" "\\Server\Share\Greetings\^!2017\^!^!NY\20170308\01.jpg"
REM Присваиваем переменной TimeToStart новое случайное значение в диапазоне от 30 до 630
SET /A TimeToStart=%RANDOM%*600/32767+90
REM Делаем задержку командой Ping, равную сгенерированному значению переменной TimeToStart
PING -n 1 -w !TimeToStart!000 192.168.11.33 > nul
REM Открываем заданную картинку 01.jpg дефолтным просмотровщиком из сетевой папки \\Server\Share\Greetings\!2017\!!NY\20170308
REM Чтобы экранировать восклицательные знаки в пути, применяем символ экранирования ^
REM Чтобы не ждать окончания выполнения команды (то есть не дожидаться закрытия просмотровщика), используем команду Start
start "Greeting 02" "\\Server\Share\Greetings\^!2017\^!^!NY\20170308\02.jpg"
REM Завершающая набор команд скобка
) 

Примечание:

Здесь нужно обратить внимание на экранирование символов восклицательного знака спецсимволом ^. Если этого не сделать, то командный процессор будет понимать все, что стоит между восклицательными знаками как переменную.

Источник: Команда SET - работа с переменными среды Windows