не срабатывает set f=%s%

Пётр252808

Новый пользователь
Сообщения
8
Симпатии
0
Баллы
1
#1
Здравствуйте!
Есть .bat-файл, который много чего делает (он простой на самом деле), но в начале есть "модуль" проверки наличия файла в папке.
Если файл с номером f уже есть, то делается +=1 и так далее, пока, наконец, не выбирается номер, на который ничего не находится,
и, значит, этот номер подходящий.
Код:
set rttnr=0
set f=1
set s=1
:donr
echo now f is %f%
echo now s is %s%
pause
if %f% LSS 10 set f=00%f%
if %f% GTR 9 (
   if %f% LSS 100 (
      set f=0%f%
)
)
echo searching for C:\Folder\%f%*.txt
pause
if exist C:\Folder\%f%*.txt (
echo %f% exists
pause
set /a s+=1
set f=%s%
echo after += f is %f%, s is %s%
pause
) else (
set nr=%f%
echo %f% does not exist, nr is %nr% exiting...
pause
set exit=1
)
if '%exit%' NEQ '1' goto donr
Блоки с echo-pause вставлены, чтобы просмотреть, что происходит в модуле.
Так вот проблема: из содержания строки
Код:
echo after += f is %f%, s is %s%
видно, что не происходит присвоения новых значений в блоке
Код:
set /a s+=1
set f=%s%
Пожалуйста, подскажите, в чём дело и как это исправить.
 
Последнее редактирование:

Dragokas

Very kind Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
5,962
Симпатии
5,784
Баллы
588
#2
Здравствуйте, @Пётр252808 !
Добро пожаловать на SafeZone.

Внутри блока (), а также в однострочниках, все операции присвоения производятся отложенно, т.е. уже после выхода из этого и всех родительских блоков ().
Есть несколько решений:
1. Отказаться от блока (). Для этого создайте подпрограмму и выполните нужные вычисления внутри неё, например:

CMD/BATCH:
if exist C:\Folder\%f%*.txt (
  call :ex1
) else (
  call :N_ex1
)
goto :eof

:ex1
  set /a s+=1
  set f=%s%
  echo after += f is %f%, s is %s%
exit /b

:N_ex1
  set nr=%f%
  echo %f% does not exist, nr is %nr% exiting...
exit /b
2. Раскрыть актуальное значение переменной, временно перейдя в подпрограмму:
CMD/BATCH:
if exist C:\Folder\%f%*.txt (
  set /a s+=1
  set f=%s%
  call :ex1
) else (
  set nr=%f%
  call :N_ex1
)
goto :eof

:ex1
  echo after += f is %f%, s is %s%
exit /b

:N_ex1
  echo %f% does not exist, nr is %nr% exiting...
exit /b
3. Задекларировать возможность отложенного раскрытия переменных с помощью спецсимвола "!" и получить !переменную!
(при этом, негативные последствия - знаки "!" будут "съедаться" в именах файлов)

CMD/BATCH:
:: добавить в начало пакетного файла
SetLocal EnableDelayedExpansion

if exist C:\Folder\%f%*.txt (
  set /a s+=1
  set f=%s%
  echo after += f is !f!, s is !s!
) else (
  set nr=%f%
  echo %f% does not exist, nr is !nr! exiting...
)
4. Получить отложенное значение переменной через промежуточный вызов нового процесса cmd с включенным режимом отложенного раскрытия:
(негативных последствий нет)
Код:
cmd /v /c echo !f!, !s!
5. Тоже самое, что и выше, только без вызова доп. процесса cmd:
(негативных последствий нет)
CMD/BATCH:
set s=1
(
  set s=2
  SetLocal EnableDelayedExpansion
  echo !f!, !s!
  SetLocal DisableDelayedExpansion
)
6. Через for + set (перечисление списка переменных):
(негативных последствий нет,
особо полезно бывает при раскрытии переменных со спецсимволами)
CMD/BATCH:
set s=1
(
  set s=2
  for /f "tokens=1* delims==" %%a in ('set s') do if "%%a"=="s" echo %%b
)
===============================
CMD/BATCH:
if %f% LSS 10 set f=00%f%
if %f% GTR 9 (
  if %f% LSS 100 (
    set f=0%f%
  )
)
можно удалить математику, добавить максимум лидирующих нулей и затем отрезать 3 последних знака:
CMD/BATCH:
@echo off

set f=88

set f=00%f%
set f=%f:~-3%

echo %f%
 

Пётр252808

Новый пользователь
Сообщения
8
Симпатии
0
Баллы
1
#3
@Dragokas, благодарю за подробный ответ!
Теперь же изучу его в ряде экспериментов!
 

Dragokas

Very kind Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
5,962
Симпатии
5,784
Баллы
588
#4
т.е. уже после выхода из этого и всех родительских блоков ().
Ну и конечно же не написал пример (но должно быть и так понятно),
что можно работать с новым значением уже после выхода из блока, т.е.:
CMD/BATCH:
if ...
(
  ...
  set /a s+=1
  ...
)
echo новое значение: %s%
Но, при этом:
CMD/BATCH:
(
set /a s+=1
set f=%s%
)
Переменной f будет присвоено старое значение s.

В понимании языка batch, при входе в блок, значения всех переменных "виртуализируются". Суда же относятся такие переменные как изменяющаяся %time%, %date% - внтури блока будут иметь всегда одинаковые значения.

Более подробно, в статье: https://safezone.cc/threads/cmd-setlocal-i-rasshirennaja-obrabotka-komand.22629/
 
Последнее редактирование:

Пётр252808

Новый пользователь
Сообщения
8
Симпатии
0
Баллы
1
#5
Уважаемый @Dragokas,
помог сразу 1й пункт Вашего первого ответа - вынос в подпрограмму,
но проблему до конца разрешить и не удалось.
После ряда экспериментов получил следующее выражение с выносом всего,
что только возможно, в подпрограммы:
Код:
set n=1
:donr
echo 1: n is %n%
pause
call :ext1
call :ext2
call :ext3
:aext3
echo 2: now searching for C:\Folder\%n%*.txt
pause
if exist C:\Folder\%n%*.txt (
echo 3: %n% exists
pause
call :ext4
goto donr
) else (
echo 5: %n% does not exist, exiting...
pause
goto exit1
)

:ext1
echo ext1-1 n is %n%
set t=%n:~0,1%
if %t%==0 set n=%n:0=%
echo ext1-2 n is %n%
pause
exit /b

:ext2
if %n% LSS 10 set n=00%n%
echo ext2 n is %n%
pause
goto aext3
exit /b

:ext3
if %n% GTR 9 (
   if %n% LSS 100 (
      set n=0%n%
   )
)
echo ext3 n is %n%
pause
exit /b

:ext4
set /a n+=1
echo ext4 n after += is %n%
pause
exit /b

:exit1
echo 6: rttnr is %rttnr%
pause
if '%rttnr%' NEQ '0' goto %rttnr%
При этом проблема "локализуется" в ext3, в котором происходит сравнение 9 < n < 100:
просчитав файл 008*.txt, скрипт внезапно сбрасывает переменную n к 1 и начинается поиск файла 001*.txt ...
В чём дело, не пойму. Помогите, пожалуйста!
 

Пётр252808

Новый пользователь
Сообщения
8
Симпатии
0
Баллы
1
#6
Переформулировка ext3 в
Код:
:ext3
if %n% GTR 9 set tt=1
if %n% LSS 100 set /a tt+=1
if %tt% EQU 2 set n=0%n%
echo ext3 n is %n%
pause
exit /b
не помогает (хотя до ext3, понятно, и дело не доходит).
Но после ext4
Код:
:ext4
set /a n+=1
echo ext4 n after += is %n%
pause
exit /b
n почему-то превращается из 8 в 1 !
 

Dragokas

Very kind Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
5,962
Симпатии
5,784
Баллы
588
#7
Ваш код очень тяжело читаем. Не знаю, как вы в нём разбираетесь, но мне очень тяжело.
Выше были всего лишь примеры, не нужно брать первый попавшийся. Логика кода стала слишком запутанной. Большинство пользуется обычно вариантом № 3.
Огромное число пауз тоже не нужно, ведь все результаты и так видны в командной строке без @echo off.
Сообщения объединены:

Использование goto - это всегда плохой стиль, от этого код становится ещё более запутанным.
Старайтесь не использовать goto вообще. Максимум, если нужно сделать какой-то бесконечный цикл.
Сообщения объединены:

Я почистил ваш код, убрал всё лишнее, в т.ч. условие с проверкой пути, т.к. у меня его нет, сами вернёте.
И получил такой ответ:

1: n is 1
ext1-1 n is 1
ext1-2 n is 1
ext2 n is 001
ext3 n is 001
ext4 n after += is 2
6: rttnr is
n = 2 как и следовало ожидать.

CMD/BATCH:
@echo off

set n=1
echo 1: n is %n%
call :ext1
call :ext2
call :ext3
call :ext4

echo 6: rttnr is %rttnr%

pause
goto :eof

:ext1
  echo ext1-1 n is %n%
  set t=%n:~0,1%
  if %t%==0 set n=%n:0=%
  echo ext1-2 n is %n%
exit /b

:ext2
  if %n% LSS 10 set n=00%n%
  echo ext2 n is %n%
exit /b

:ext3
  if %n% GTR 9 (
     if %n% LSS 100 (
        set n=0%n%
     )
  )
  echo ext3 n is %n%
exit /b

:ext4
  set /a n+=1
  echo ext4 n after += is %n%
exit /b
Сообщения объединены:

Только зачем столько подпрограмм, я не понимаю.
Вам нужна была только одна, там, где условная проверка.
Сообщения объединены:

P.S. Приравнивание надежнее делать так:
Код:
if "%rttnr%" NEQ "0"
или
Код:
if "%rttnr%"=="0"
Сообщения объединены:

Если файл с номером f уже есть, то делается +=1 и так далее, пока, наконец, не выбирается номер, на который ничего не находится,
и, значит, этот номер подходящий.
Если я правильно понял вашу задачу, то она решается так:
CMD/BATCH:
@echo off
SetLocal EnableExtensions EnableDelayedExpansion

For /L %%C in (0,1,999) do (
  set n=00%%C
  set n=!n:~-3!
  if not exist "C:\Folder\!n!*.txt" goto ext
)
:ext
echo Первый свободный номер = %n%

pause
 
Последнее редактирование:

Пётр252808

Новый пользователь
Сообщения
8
Симпатии
0
Баллы
1
#8
Если я правильно понял вашу задачу, то она решается так:
@Dragokas, огромное спасибо за изящное решение всего моего вопроса!


Только зачем столько подпрограмм, я не понимаю.
Этим я пытался обойти проблему, указанную Вами в
Внутри блока (), а также в однострочниках, все операции присвоения производятся отложенно, т.е. уже после выхода из этого и всех родительских блоков ().
Решение при помощи подпрограмм показалось мне самым понятным и
я просто вынес все участки, показавшиеся мне потенциально проблемными в связи с их нахождением внутри блоков,
в подпрограммы. Постараюсь в будущем следовать Вашим рекомендациям!


Нижестоящее есть лишь моё собственное мнение.
Предполагаю, что "EnableExtensions" и "EnableDelayedExpansion" являются следствием исторического развития системы DOS,
типа постепенного развития технических возможностей вычислительной техники (хотя тоже кажется странным,
что когда-либо существовали технические ограничения, затрагивавшие раскрытие переменных в коде),
а не попыткой упрятать от пользователя некоторые полезные рабочие свойства (раскрытие переменных) системы.
По-моему это нормально при человеческом прочтении кода считать, что в коде, например,
Код:
(
...
set n=!n:~-3!
...
)
превращения переменной "n" происходят именно во время прохождения строки
Код:
set n=!n:~-3!
(по крайней мере пользователю не приходится стакиваться с последствиями какой-либо невидимой постобработки),
а не где-то "за скобками" в неопределённый момент времени.
Впрочем, наверное, были какие-то основания сконструировать DOS именно так, а не иначе. :Dntknw:
 

Dragokas

Very kind Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
5,962
Симпатии
5,784
Баллы
588
#9
Я наверное не смогу ответить на этот вопрос. Возможно, это как-то связано с особенностями интерпретации кода при его последовательной обработке. Разбор происходит не построчно, а блоками.
Как было раньше не скажу. Надо спрашивать у старичков. Во времена Win 9x вроде еще не было этих директив и вообще в принципе расширенных возможностей команд, как доп. ключи у for.
 

Dragokas

Very kind Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
5,962
Симпатии
5,784
Баллы
588
#11
В темах по программированию у нас такая возможность не предусмотрена.
 
Сверху Снизу