Как считать строку из вывода cmd и вернуть значение в пакетном скрипте?

Вопрос или проблема

Мой сервер Windows подключен к сети удаленного офиса через два шлюза по умолчанию.

Шлюзы по умолчанию

172.20.0.1    Первичный
172.20.0.254  Вторичный

// Вторичный подключен с
// 4G SIM-картой VPN 

Начальная конфигурация сервера

172.20.0.10   IP-адрес
255.255.255.0 Маска подсети
172.20.0.1    Шлюз по умолчанию

Я хочу запустить пакетный скрипт на сервере, который обнаруживает, когда первичный шлюз 172.20.0.1 выходит из строя, и автоматически переключает сервер на вторичный шлюз 172.20.0.254 и наоборот.

Я хочу обнаруживать сбой, регулярно пингую текущий шлюз.

Пинг 172.20.105.1 с 32 байтами данных:

Ответ от 172.20.0.1: байты=32 время<1мс TTL=255

Ответ от 172.20.0.1: байты=32 время=1мс TTL=255

Ответ от 172.20.0.1: байты=32 время=1мс TTL=255

Когда пинг не проходит, он не возвращает “TTL=”, и скрипт предполагает, что шлюз отключен, и переключается на другой шлюз.

@echo off
setlocal enableextensions enabledelayedexpansion

:loop
ping 172.20.0.1 -n 2
timeout /t 10
set "str1=%~1"

if not "%str1:"TTL="=%" == "%str1%" (
     echo "Первичный шлюз НЕДОСТУПЕН"
     netsh interface ipv4 set address name="Ethernet" static 172.20.0.10 255.255.255.0 172.20.0.254   
    )

echo "Данные первичного шлюза ДЕЙСТВИТЕЛЬНЫ"
goto loop

Есть ли более хороший способ сделать это на стороне сервера?

-// ИЗМЕНЕНИЕ //-

Мой более короткий совет:

@echo off && pushd "C:\Windows\System32"
set "_iPs=static 172.20.0.2 255.255.255.0 172.20.0.254"

:loop
  =;( ping 172.20.0.1 -n 2 | findstr TTL^= | find /c /v "" );= | find "2" >nul && =;(
      timeout 060 /nobreak | echo/Сервер данных отключен, пожалуйста подождите... );= || =;( 
      echo/Сервер данных отключен, пожалуйста подождите... переключение на резервный сервер.
      netsh interface ipv4 set address name="Ethernet" %_iPs%
    );= & goto loop & cls

@echo off && setlocal enabledelayedexpansion

set "_gtw#254=netsh interface ipv4 set address name="Ethernet" static 172.20.0.10 255.255.255.0 172.20.0.254"
set "_gtw#001=!_gtw#254:172.20.0.254=172.20.0.1!"
set "_msg#001=Первичный шлюз НЕДОСТУПЕН"
set "_msg#002=Данные первичного шлюза ДЕЙСТВИТЕЛЬНЫ"

mode.com con: cols=60 lines=5
for /f tokens^=1*delims^=#skip^=4 %%i in ('echo;prompt $E#$H^|cmd.exe
    ')do set "_$E=%%~i" & set "_bs=%%~j"

%:^)
title <nul
set "_dns=" <nul
echo/!_$E![1F!_$E![0J

for /f tokens^=2*delims^=: %%i in ('netsh interface ipv4 show config ^| findstr "DNS.*DHCP.*[0-9]"
   ')do for /f %%n in ('%ComSpec% /u /c echo="%%~i"^<nul ^| find /v " "')do set "_dns=!_dns!%%~n"

title Шлюз: !_dns!
echo/!_bs!!_$E![1;33m Пожалуйста^^! Подождите несколько секунд...!_$E![0m

ping !_dns! -n 6 -w 500 | find /c "TTL" | find "6" >nul && goto %:^) || =;(
     if "!_dns!" == "172.20.0.1" 2>nul =;(
           echo; !_msg#001!
           !_gtw#254!
           echo; !_msg#002:Первичный=Вторичный!
        ); else 2>nul =;( 
           echo; !_msg#001:Первичный=Вторичный!
           !_gtw#001!
           echo; !_msg#002!
        );=
    );= 

timeout /t 00010 /nobreak >nul 
echo/!_$E![1F!_$E![0J!_$E![1F!_$E![0J!_$E!
goto %:^)

Или…

@echo off && setlocal enabledelayedexpansion

set "_gtw#254=static 172.20.0.10 255.255.255.0 172.20.0.254"
set "_msg#001=Первичный шлюз НЕДОСТУПЕН" 
set "_msg#002=Данные первичного шлюза ДЕЙСТВИТЕЛЬНЫ"

set "_gtw#254=netsh interface ipv4 set address name="Ethernet" !_gtw#254!"
set "_gtw#001=!_gtw#254:172.20.0.254=172.20.0.1!"

for /f tokens^=1*delims^=#skip^=4 %%i in ('echo;prompt $E#$H^|cmd.exe
    ')do set "_$E=%%~i" & set "_bs=%%~j" & mode.com con: cols=60 lines=5

%:^)
title <nul & set "_dns=" <nul & echo/!_$E![1F!_$E![0J

for /f tokens^=2*delims^=: %%i in ('netsh interface ipv4 show config ^| findstr "DNS.*DHCP.*[0-9]"
   ')do for /f %%n in ('%ComSpec% /u /c echo="%%~i"^<nul ^| find /v " "')do set "_dns=!_dns!%%~n"

title Шлюз: !_dns! & echo/!_$E![1;33m Пожалуйста^^! Подождите несколько секунд...!_$E![0m

ping !_dns! -n 6 -w 500 | find /c "TTL" | find "6" >nul && goto %:^) || =;(
     if "!_dns!" == "172.20.0.1" 2>nul =;(echo; !_msg#001! & !_gtw#254! & echo; !_msg#002:Первичный=Вторичный!
        ); else 2>nul =;( echo; !_msg#001:Первичный=Вторичный! & !_gtw#001! & echo; !_msg#002! );=
    );= & timeout /t 00010 /nobreak >nul & echo/!_$E![1F!_$E![0J!_$E![1F!_$E![0J!_$E! & goto %:^)

Есть ли более хороший способ сделать это на стороне сервера?

  • Да, если я учту приведенные ниже моменты:

1. Ваш код в вопросе не определяет, является ли текущий шлюз первичным, и было ли уже выполнено переключение на вторичный шлюз.


2. Переключение шлюза с вторичного на первичный никогда не произойдет в случае сбоя вторичного шлюза.


3. Ваши echo и netsh команды будут выполняться, только если ваш первичный используется и выходит из строя соответствующим образом, но только в этом условии первый используется и в первый раз в цикле, после этого второй шлюз будет всегда повторно настроен для использования, даже когда отключен.


4. %~1 в set "str1=%~1" не имеет смысла в вашем вопросе из-за отсутствия ссылки на аргумент(ы), используемые и/или цели сравнения, где аргумент %~1 определяет переменную str1, и ее подстрока ("%str1:"TTL="=%") сравнивается:

set "str1=%~1"

if not "%str1:"TTL="=%" == "%str1%"

P.S.С этого момента обращайтесь к коду в вашем ответе...


5. Пункты, указанные в вышеупомянутых пунктах 1., 2. и 3., также повторяются в ваших действиях/эффектах в вашем текущем ответе.


6. Нет необходимости создавать файл при перенаправлении вашей команды ping, и замена, где вывод опускается с >nul, а перенаправление к оператору && (возврат 0), || (возврат не 0) может дать тот же результат без дополнительных ìfs, помня, что при закрытии , созданный файл останется там без использования.

ping 172.20.0.1 -n 2 > pingtest.txt
 if %errorlevel%==0 goto waiting

ping 172.20.0.1 -n 2 >nul && goto waiting

P.S.С этого момента обращайтесь к версии #1 этого ответа

Будет произведено будущее изменение с добавлением описаний действий/команд, но это будет сделано в мое свободное время.


a) Определите ваши переменные и также разрешите использование подстрок в этом определении или ваших командах, что позволяет сделать соответствующее изменение для Шлюз .1 и Шлюз .254 с:

set "_gtw#254=netsh interface ipv4 set address name="Ethernet" static 172.20.0.10 255.255.255.0 172.20.0.254"
set "_gtw#001=!_gtw#254:172.20.0.254=172.20.0.1!"
set "_msg#001=Первичный шлюз НЕДОСТУПЕН"
set "_msg#002=Первичный Данные ДЕЙСТВИТЕЛЬНЫ"
...
if "!_dns!" == "172.20.0.1" 2>nul =;(
           echo; !_msg#001!
           !_gtw#254!
           echo; !_msg#002:Первичный=Вторичный!
           );= else =;( 
           echo; !_msg#001:Первичный=Вторичный!
           !_gtw#001!
           echo; !_msg#002!
           );=

b) Получите вывод команды netsh interface ipv4 show config в первом цикле for /f, и используйте соответствующий токен/разделитель, чтобы получить IP DNS/Шлюза, используемого в данный момент, а во втором цикле for /f отфильтруйте/удалите любые пробелы с find /aVoid " " ... :

for /f tokens^=2*delims^=: %%i in ('netsh interface ipv4 show config ^| findstr "DNS.*DHCP.*[0-9]"
   ')do for /f %%n in ('%ComSpec% /u /c echo="%%~i"^>nul ^| find /v " "')do set "_dns=!_dns!%%~n"

c) Используемая команда ping займет около 10 секунд, поэтому можно использовать заголовок с отображением уже полученного DNS/Шлюза и дополнительно какое-то сообщение, отображаемое во время этого времени, чтобы дать пользователю знак, что идет активность:

введите описание изображения здесь

title Шлюз: !_dns!
echo/!_bs!!_$E![1;33m Пожалуйста^^! Подождите несколько секунд...!_$E![0m

d) В случае кода, который будет работать в :loop, откуда я предполагаю, что его использование не случайно и что его выполнение будет продолжаться некоторое время, нет причин не иметь некоторой дополнительной доработки, такой как цвет текста, используя ANSI, и вместо того, чтобы очищать весь экран с cls, делать это только на соответствующих строках, помимо того, чтобы поддерживать экран в размерах “отрегулированных” mode для текста, который будет отображаться:


for /f tokens^=1*delims^=#skip^=4 %%i in ('echo;prompt $E#$H^|cmd.exe
    ')do set "_$E=%%~i" & set "_bs=%%~j" & mode.com con: cols=60 lines=5

:: $E установить _$E [%%~i] в ESC Символ 
:: $H установить _bs [%%~j] в Символ BackSpace

:: Отображаемый размер/буфер - количество колонок и количество строк:
:: MODE    CON[:] [COLS=c] [LINES=n]
:: mode.com con:   cols=60  lines=5
title >nul & ... echo/!_$E![1F!_$E![0J 
...
title Шлюз: ... echo/!_$E![1;33m Пожалуйста^^! Подождите несколько секунд...!_$E![0m

e) Установите ping число (-n 6) и подсчитайте (find /Count) количества выходов TTL, используя перенаправитель | и пропуская вывод на экране (>nul), также в этом процессе обработки команд сделайте выгодное использование операторов && и ||, чтобы определить соответствующие действия в зависимости от возврата 0 и возврата не 0:

ping !_dns! -n 6 | find /c "TTL" | find "6" <nul && .. || ...

ваша команда ping && (
     echo/ Вернулся 0  
    )  ||  (
     echo/ Вернулся не 0
    )  

ping !_dns! -n 6 -w 500 | find /c "TTL" | find "6" >nul && goto %:^)
ping !_dns! -n 6 -w 500 | find /c "TTL" | find "6" >nul || if condition() else()

f) Использование оператора в условии возврат 0, чтобы вернуться к перезапуску действий по получению DNS/Шлюза, и снова ping с timeout соответствующего интервала, в случае если количество “TTL” не совпадает, if принимает условие, чтобы возврат не 0, и определяет текущий DNS/Шлюз, предполагая компоновку текста и соответствующей команды, используя varible:substrings=?, относящиеся к условию, компонуя соответствующий текст и команду:

if "!_dns!" == "172.20.0.1" 2>nul =;(
           echo; !_msg#001!
           !_gtw#254!
           echo; !_msg#002:Первичный=Вторичный!
           );= else =;( 
           echo; !_msg#001:Первичный=Вторичный!
           !_gtw#001!
           echo; !_msg#002!
           );=


g) После действий, обработанных в вышеупомянутых пунктах, timeout остается для выполнения командного цикла для новых диагностик и соответствующих действий, основанных на возвращаемых командах, затем ожидание, за которым следует очистка (echo/ ESC[ANSI коды) использованных строк и перенаправление на goto метку %:^), которая запускает новый цикл, идеально соответствующий этим действиям:

timeout /t 00010 /nobreak >nul 
echo/!_$E![1F!_$E![0J!_$E![1F!_$E![0J!_$E!
goto %:^)


Читать далее:

Спасибо всем. Я нашел более короткий способ для этого.
Вот как..

@echo off

cls
:starting
ping 172.20.0.1 -n 2 > pingtest.txt

c:\windows\system32\findstr.exe "TTL=" pingtest.txt

if %errorlevel%==0 goto waiting

echo "ДАННЫЕ ОТОКЛЮЧЕНЫ, переключаюсь на РЕЗЕРВНУЮ СЕТЬ"

netsh interface ipv4 set address name="Ethernet" static 172.20.0.2 255.255.255.0 172.20.0.254   

timeout /t  60

:waiting

echo "ДАННЫЕ РАБОТАЮТ, жду задержку"
timeout /t  60

goto starting

Ответ или решение

Для автоматизации переключения маршрутизаторов на сервере Windows с использованием пакетного скрипта, который будет реагировать на недоступность основного шлюза (172.20.0.1), необходимо создать скрипт, который будет периодически проверять соединение с основным шлюзом с помощью команды ping. Если соединение не удалось, скрипт должен переключиться на вторичный шлюз (172.20.0.254). Давайте создадим наиболее оптимизированный и понятный скрипт для решения данной задачи.

Вот пример пакетного скрипта для реализации описанной логики:

@echo off
setlocal enabledelayedexpansion

:: Определяем IP-адреса и настройки
set "PRIMARY_GATEWAY=172.20.0.1"
set "SECONDARY_GATEWAY=172.20.0.254"
set "INTERFACE_NAME=Ethernet"
set "IP_ADDRESS=172.20.0.10"
set "SUBNET_MASK=255.255.255.0"

:loop
cls
echo Проверка основного шлюза: %PRIMARY_GATEWAY%
ping -n 2 %PRIMARY_GATEWAY% | find "TTL=" >nul
if !errorlevel! equ 0 (
    echo Основной шлюз доступен.
) else (
    echo Основной шлюз недоступен, переключение на вторичный шлюз.
    netsh interface ipv4 set address name="%INTERFACE_NAME%" static %IP_ADDRESS% %SUBNET_MASK% %SECONDARY_GATEWAY%
    timeout /t 10
    goto loop
)

echo Ожидание 60 секунд перед следующей проверкой...
timeout /t 60
goto loop

Описание работы скрипта:

  1. Инициализация переменных: В начале скрипта определяются переменные для основных IP-адресов, маски подсети и имени интерфейса.

  2. Главный цикл проверки: Используется метка :loop для организации бесконечного цикла. В этом цикле происходит очистка экрана, выполнение команды ping для проверки доступности основного шлюза.

  3. Проверка результатов команды ping:

    • Если ping успешен (возвращает TTL=), выводится сообщение о доступности основного шлюза.
    • Если ping не удался, выполняется переключение на вторичный шлюз с помощью команды netsh.
  4. Таймер: После проверки, если основной шлюз доступен, запускается таймер на 60 секунд, после чего снова начинается проверка.

Рекомендации по дополнениям:

  • Если требуется не только переключение на вторичный шлюз, но и обратно на основной, можно сохранить информацию о текущем активном шлюзе и при последующих проверках, если основной оказывается доступным, возвращать его обратно.
  • Для повышения надежности, можно рассмотреть возможность использования более сложной проверки (например, добавление дополнительных ping для ожидания до полной доступности).

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

Оцените материал
Добавить комментарий

Капча загружается...