Пакетные файлы Windows: установка переменной в цикле for - PullRequest
24 голосов
/ 11 апреля 2011

У меня есть несколько файлов с одинаковой схемой именования. Например, четыре файла называются «num_001_001.txt», «num_002_001.txt», «num_002_002.txt», «num_002_003.txt»

Первый набор чисел представляет, из какого «пакета» он, а второй набор чисел просто используется, чтобы отличать их друг от друга. Таким образом, в этом примере у нас есть один файл в пакете 001 и три файла в пакете 002.

Я пишу пакетную команду Windows Vista, чтобы взять все файлы и переместить их в свои собственные каталоги, где каждый каталог представляет отдельный пакет. Поэтому я хочу переместить все файлы для пакета 001 в каталог "001" и все для 002 в каталог "002"

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

Теперь я понял, что мне нужно будет использовать подстроки, поэтому я использовал синтаксис% var: ~ start, end% для их получения. В качестве теста я написал это, чтобы убедиться, что я действительно могу извлечь подстроку и условно создать каталог

@echo off
set temp=num_001_001.txt
NOT IF exist %temp:~0,7%\
  mkdir %temp:~0,7%

И это работает. Отлично.
Тогда я добавил цикл for.

@echo off
FOR /R %%X IN (*.txt) DO (
  set temp=%%~nX
  echo directory %temp:~0,7%
)

Но это мой вывод:

directory num_002
directory num_002
directory num_002
directory num_002

Что не так? Разве Vista не поддерживает переназначение переменных в каждой итерации? Четыре файла находятся в моем каталоге, и один из них должен создать num_001. Я положил в разные файлы с 003 004 005, и все это было имя последнего пакета. Я предполагаю, что что-то не так с тем, как я настраиваю вещи.

У меня есть разные обходные пути для выполнения работы, но я сбит с толку, почему такая простая концепция не сработает.

Ответы [ 3 ]

54 голосов
/ 11 апреля 2011

Ваша проблема в том, что переменная заменяется, когда пакетный процессор читает команду for перед ее выполнением.

Попробуйте:

SET temp=Hello, world!
CALL yourbatchfile.bat

И вы увидите Hello напечатано5 раз.

Решение с задержкой расширения;сначала необходимо включить его, а затем использовать !temp! вместо %temp%:

@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R %%X IN (*.txt) DO (
  set temp=%%~nX
  echo directory !temp:~0,7!
)

См. здесь для получения более подробной информации.

14 голосов
/ 11 апреля 2011

Другое решение - переместить тело цикла for в подпрограмму и call it.

@echo off
FOR /R %%X IN (*.txt) DO call :body %%X
goto :eof

:body
set temp=%~n1
echo directory %temp:~0,7%
goto :eof

Зачем это? Одна из причин заключается в том, что командный процессор Windows жаден до скобок, и результаты могут быть удивительными. Я обычно сталкиваюсь с этим при разыменовании переменных, которые содержат C:\Program Files (x86).

Если командный процессор Windows был менее жадным, следующий код вывел бы либо One (1) Two (2), либо ничего вообще:

@echo off
if "%1" == "yes" (
   echo 1 (One)
   echo 2 (Two)
)

Однако это не то, что он делает. Либо он печатает 1 (One 2 (Two), в котором отсутствует ), либо печатает 2 (Two). Командный процессор интерпретирует ) после One как конец тела оператора if, обрабатывает второй echo, как если бы он находился вне оператора if, и игнорирует окончательный ).

4 голосов
/ 11 апреля 2011

Я не уверен, официально ли это задокументировано, но вы можете смоделировать отложенное расширение, используя оператор call:

@echo off
FOR /R %%X IN (*.txt) DO (
  set temp=%%~nX
  call echo directory %%temp:~0,7%%
)

Удвоение знака процента откладывает подстановку переменной для второй оценки.Однако отсроченное расширение намного проще.

...