В чем конкретно заключается проблема с работой цикла FOR?

У меня возникла задача отключения компьютеров по расписанию с помощью скрипта CMD. Я написал следующий код:

<pre><code class="dos">Setlocal EnableDelayedExpansion
@ECHO OFF
SET file=hosts.txt
SET username=megoodminЪ
SET pass=megoodminЪ_password
FOR /F %%i IN (%file%) DO (
	SET tmpip=%%i
	NET USE \\!!tmpip! %pass% /USER:%username%
	IF errorlevel 1 GOTO fail
	REM SHUTDOWN /R /T 30 /F /M \\!!tmpip!
	IF errorlevel 1 GOTO fail
	ECHO Shutdown !!tmpip! success
	REM GOTO next
	:fail
	ECHO Shutdown !!tmpip! failed
	:next
	NET USE \\!!tmpip! /DELETE
)</code></pre>

На данный момент я закомментировал строку с командой shutdown и заменил параметр /s на /r для тестирования. Однако, после успешного выполнения основного блока циклу следующая инструкция после метки fail все равно выполняется — логирует сообщение об ошибке. Если убрать REM перед GOTO, как показано ниже:

<pre><code class="dos">Setlocal EnableDelayedExpansion
@ECHO OFF
SET file=hosts.txt
SET username=megoodminЪ
SET pass=megoodminЪ_password
FOR /F %%i IN (%file%) DO (
	SET tmpip=%%i
	NET USE \\!!tmpip! %pass% /USER:%username%
	IF errorlevel 1 GOTO fail
	REM SHUTDOWN /R /T 30 /F /M \\!!tmpip!
	IF errorlevel 1 GOTO fail
	ECHO Shutdown !!tmpip! success
	GOTO next
	:fail
	ECHO Shutdown !!tmpip! failed
	:next
	NET USE \\!!tmpip! /DELETE
)</code></pre>

то после успешного выполнения основного блока код переходит к метке next, и, соответственно, цикл завершается. Я не понимаю, в чем может быть ошибка. Возможно, я чего-то не знаю о порядке выполнения команд в CMD или мне нужно сменить профессию...
  • 18 сентября 2024 г. 9:03
Ответы на вопрос 2
Ваша проблема заключается в области видимости меток и контроля потока в командном интерпретаторе Windows (CMD). Ваша логика подразумевает использование меток для управления выполнением кодовых блоков, но в командных циклах `FOR` поведение отличается от обычных условных операторов.

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

### Основные моменты:
1. **Область видимости меток:** Каждая метка, которую вы определяете (например, `:fail` и `:next`), имеет локальную область видимости в теле цикла `FOR`. Если вы используете `GOTO`, он может выйти из цикла, но не перейдет к следующей итерации. Это приведет к тому, что код после метки `:next` не будет выполняться, и выполнение вернется в начало цикла.

2. **Логика управления потоком:** Вместо использования `GOTO` между метками, можно использовать переменные для управления потоком выполнения.

### Пример исправленного кода:
```cmd
@ECHO OFF
Setlocal EnableDelayedExpansion
SET file=hosts.txt
SET username=megoodminЪ
SET pass=megoodminЪ_password

FOR /F %%i IN (%file%) DO (
    SET tmpip=%%i
    NET USE \\!!tmpip! %pass% /USER:%username%
    IF errorlevel 1 (
        ECHO Shutdown !!tmpip! failed
        NET USE \\!!tmpip! /DELETE
        GOTO next
    )
    
    REM SHUTDOWN /R /T 30 /F /M \\!!tmpip!
    IF errorlevel 1 (
        ECHO Shutdown !!tmpip! failed
    ) ELSE (
        ECHO Shutdown !!tmpip! success
    )
    
    NET USE \\!!tmpip! /DELETE
    :next
)
```

### Основные изменения:
- Вложенные блоки if используют скобки `()` для явного определения, что следует делать в случае ошибки.
- Убраны метки `:fail` и `:next` для упрощения логики. Вместо этого осуществляется только прерывание текущей итерации с помощью `GOTO next`.
- Пользуйтесь `NET USE \\!!tmpip! /DELETE` только один раз после обработки IP и возможного отключения.

Теперь после неудачной попытки подключения к компьютеру через `NET USE`, выполнение переходит к следующему IP из файла, а не к меткам, что обеспечивает корректное поведение цикла.
Решил не усложнять, написать на CMD
да и на Powershell не сложнее...
$Comps = Get-Content -Path hosts.txt
$Cred = Get-Credential -Credential YourDomain\megoodminЪ
Stop-Computer -ComputerName $Comps -Force -Credential $Cred
Похожие вопросы