Пакетный файл может быть записан так:
@echo off
echo %DATE% %TIME%
setlocal EnableExtensions EnableDelayedExpansion
set "DNS1=8.8.8.8"
set "DNS2=8.8.4.4"
for %%i in (%DNS1% %DNS2%) do (
echo ----------------
if %%i == 8.8.8.8 set "host=Google1"
if %%i == 8.8.4.4 set "host=Google2"
echo tracert to !host!
tracert -d %%i
)
endlocal
echo %TIME%
exit /B
Командный процессор Windows выполняет одну командную строку за другой.Как синтаксический анализ командной строки перед выполнением описан в Как синтаксический анализ сценариев интерпретатора команд Windows (CMD.EXE)? Что в конечном итоге выполняется после анализа командной строки, можно увидеть при выполнении пакетного файла без @echo off
из окна командной строки, как описано в отладка командного файла .
Блок команд, начинающийся с (
и заканчивающийся соответствием )
, полностью анализируется cmd.exe
перед выполнением команды делает условное или безусловное использование командного блока.Во время синтаксического анализа командного блока все ссылки на переменные среды, использующие синтаксис %variable%
наподобие %host%
, заменяются текущим значением указанной переменной среды.В этом случае %host%
, скорее всего, заменяется пустой строкой до того, как FOR вообще будет выполнен, если переменная окружения host
не определена случайно при запуске пакетного файла.
Вывод справки при запуске set /?
в окне командной строки объясняет, когда и как использовать отложенное расширение на примере IF и FOR .В приведенном выше коде расширение переменной среды с задержкой включено и используется для ссылки на текущее значение переменной среды host
на итерациях блока команд тела цикла, для которого определяется переменная среды и ей присваивается строковое значение.
Также можно было бы использовать:
@echo off
echo %DATE% %TIME%
setlocal EnableExtensions DisableDelayedExpansion
set "DNS1=8.8.8.8"
set "DNS2=8.8.4.4"
for %%i in (%DNS1% %DNS2%) do (
echo ----------------
if %%i == 8.8.8.8 set "host=Google1"
if %%i == 8.8.4.4 set "host=Google2"
call echo tracert to %%host%%
tracert -d %%i
)
endlocal
echo %TIME%
exit /B
Командная строка call echo tracert to %%host%%
изменяется на этапе синтаксического анализа всего командного блока командным процессором Windows на call echo tracert to %host%
и из-за команды CALL эта командная строка анализируется второй раз при каждом выполнении командного блока перед выполнением команды ECHO , в результате чего в консольное окно выводится текущее значение переменной среды host
.
Но я предлагаю использовать этот гораздо лучший код:
@echo off
echo %DATE% %TIME%
echo/
setlocal EnableExtensions DisableDelayedExpansion
set "DNS1=8.8.8.8=Google1"
set "DNS2=8.8.4.4=Google2"
for /F "tokens=2* delims==" %%I in ('set DNS 2^>nul') do (
echo ----------------
echo tracert to %%J
echo %SystemRoot%\System32\tracert.exe -d %%I
)
endlocal
echo/
echo %TIME%
exit /B
Один или несколько IP-адресов и их имена хостов назначаются одной или нескольким переменным среды, начинающимся со строки DNS
.IP-адрес и имя хоста разделяются знаком равенства.
Команда FOR выполняется в отдельном командном процессе, запущенном с cmd.exe /C
в фоновом режиме командной строки:
set DNS 2>nul
Команда SET выводит для обработки STDOUT (стандартный вывод) все переменные среды, начиная с DNS
, отсортированные в алфавитном порядке с name=value
, что означает для этого примера:
DNS1=8.8.8.8=Google1
DNS2=8.8.4.4=Google2
Сообщение об ошибке, выводимое SET для обработки STDERR (стандартная ошибка) при отсутствии поиска переменной среды, начинающейся со строки DNS
, будет подавлено этим кодом путем перенаправленияэто на устройство NUL .
Прочтите также статью Microsoft о Использование операторов перенаправления команд для объяснения 2>nul
.Оператор перенаправления >
должен быть экранирован с помощью символа вставки ^
в командной строке FOR , чтобы интерпретироваться как литеральный символ, когда интерпретатор команд Windows обрабатывает эту командную строку перед выполнением команды FOR , котораявыполняет встроенную командную строку set
в отдельном командном процессе, запущенном в фоновом режиме.
FOR с использованием опции /F
захватывает все выходные данные для обработки STDOUT в фоновом режимеКоманда обрабатывает и обрабатывает эти выходные данные, игнорируя пустые строки и строки, начинающиеся с точки с запятой.
Все захваченные строки начинаются с DNS
, и поэтому определенно не игнорируется ни одна строка, поскольку ни одна строка не начинается с ;
.
FOR с использованием опции /F
также будет разбивать каждую строку на подстроки с использованием пробела / табуляции в качестве разделителя с назначением только первой подстроки указанной переменной цикла I
.Но такое поведение разделения строк бесполезно для этой задачи.
По этой причине строка параметров "tokens=2* delims=="
переопределяет поведение разделения строк.Теперь =
используется в качестве разделителя между строками вместо пробела и табуляции.
И вместо назначения первой строки =
с разделителями для переменной цикла I
, которая будет именем переменной среды,вторая строка с разделителями со знаком равенства назначается переменной цикла I
из-за tokens=2
, который является IP-адресом.
И дополнительно назначается следующей переменной цикла J
в соответствии с таблицей ASCII остаток строки после знака (ов) после второй =
строки с разделителями без дальнейшего разделения строки на знак равенства.Таким образом, имя хоста присваивается переменной цикла J
, даже если она содержит один или несколько =
, если они не содержатся в начале имени хоста.
Этот код, очевидно, лучше, чем DNS
Переменные среды могут быть определены в верхней части пакетного файла с IP-адресом и именем хоста, и ничего не должно быть изменено в командной строке ниже, потому что он просто обрабатывает от 0 до n переменных среды, начиная с DNS
и имея по крайней мере секунду =
подстрока с разделителями.
Чтобы понять используемые команды и то, как они работают, откройте окно командной строки, выполните там следующие команды и полностью прочитайте все страницы справки, отображаемые для каждой команды.
call /?
echo /?
endlocal /?
exit /?
for /?
if /?
set /?
setlocal /?
tracert /?