Как работает FOR в командном файле cmd?

Я программировал на десятках языков в течение 20 лет, но никогда не мог понять, как работает «FOR» в командном файле командной оболочки Windows, как бы я ни старался. Я читаю

http://www.amazon.com/Windows-Administration-Command -Line-Vista/dp/0470046163/ref=sr

rem showpathenv.bat
for /f "delims=;" %%g in ("%PATH%") do echo %%g
1?ie=UTF8&s=books&qid=1241362727&sr=8-1

http://www.ss64.com/nt/for.html

и несколько других статей в Интернете, но все еще путают и ничего не могут сделать.

Может ли кто-нибудь дать мне краткое объяснение того, как работает FOR в целом?

Для более конкретного вопроса, как я могу перебирать каждый путь в переменной% PATH%? я пробовал с

rem showpathenv.bat
for /f "delims=;" %%g in ("%PATH%") do echo %%g

Это покажет только первый путь, а не все. Почему ? Что я делаю неправильно?


person Sake    schedule 03.05.2009    source источник
comment
в «принятом» ответе отсутствуют некоторые важные функции, доступные для цикла for /f. Смотрите мой собственный ответ ... Я думаю, что он намного ближе к тому, о чем вы просили, чем ответ Марка. Но тогда я не уверен на 100%, что вы просили ;-)   -  person Kurt Pfeifle    schedule 08.08.2010
comment
Никто не ответил на ваш вопрос: 'Почему? Что я делаю не так?' -- Итак, я попробую сейчас: он показывает вам только первый путь, потому что вы только просили об этом. Он работает так, как задумано (я не говорю, что дизайн хорош). Вы должны попросить больше, если хотите больше: после «g» в алфавите идет «h» — поэтому добавьте echo %%h & echo %%i & echo %%j, чтобы увидеть еще 3 пути к каталогам.   -  person Kurt Pfeifle    schedule 08.08.2010
comment
@pipitas and tokens=... C:\›for /f tokens=1,2,3,4 delims=; %g в (%PATH%) сделать эхо %g %h %i   -  person barlop    schedule 20.09.2011
comment
Ответ @pipitas сакэ выглядит довольно гибким, ему не нужен длинный список токенов, для обработки пути используется замена (см. set /?).   -  person barlop    schedule 20.09.2011


Ответы (12)


Ни один из ответов на самом деле не работает. Мне удалось найти решение самостоятельно. Это немного хакерски, но это решает проблему для меня:

echo off
setlocal enableextensions
setlocal enabledelayedexpansion
set MAX_TRIES=100
set P=%PATH%
for /L %%a in (1, 1, %MAX_TRIES%) do (
  for /F "delims=;" %%g in ("!P!") do (
    echo %%g
    set P=!P:%%g;=!
    if "!P!" == "%%g" goto :eof
  )
)

Ой ! Я ненавижу программирование пакетных файлов!

Обновлено

Решение Марка проще, но оно не будет работать с путями, содержащими пробелы. Это немного измененная версия решения Марка.

echo off
setlocal enabledelayedexpansion
set NonBlankPath=%PATH: =#%
set TabbedPath=%NonBlankPath:;= %
for %%g in (%TabbedPath%) do (
  set GG=%%g
  echo !GG:#= !
)
person Sake    schedule 04.05.2009
comment
это гениально.. кажется, это осталось незамеченным. Вы должны принять свой собственный ответ. Очень инновационно, использовать 2 цикла, один как счетчик, а второй с использованием подстановки, чтобы пройти путь, заменяя обработанные биты ничем, в качестве альтернативы токенам = 1,2,3,4.... очень приятно. - person barlop; 20.09.2011
comment
Спасибо, барлоп. Да, это лучший ответ, но мне стыдно принимать свой ответ :). Удивительно, что вы можете понять код. Я больше не могу понять, что я сделал сам. :) - person Sake; 21.09.2011
comment
Примите свой собственный, потому что он лучший, а принятый ответ больше для базы данных, чем для кого-то одного. - person barlop; 21.09.2011
comment
Как ни странно, мне просто пришлось сделать летучую мышь, чтобы повторять каждый DIR на пути ... не знал, как, но проверил ваш. сделал это, чтобы я мог видеть, есть ли дубликаты. Я воспользовался вашей техникой. Я сделал это (установил a=%PATH: =#%) & (установил b=%a:;= %) и для %f в (%b%) сделал @echo %f Не возражал, что каждый каталог с у пробела был #, для моего случая. Тогда я сделал | СОРТИРОВАТЬ. - person barlop; 28.09.2011
comment
на самом деле просматривая есть несколько хороших ответов. Посмотрите на это из ответа Джея, это можно сделать в одной строке C:\›для %p в (%path:;= %) do @echo %~p - person barlop; 28.09.2011

Идея Марка была хорошей, но, возможно, он забыл, что в некоторых путях есть пробелы. Замена ';' с '" "' вместо этого разрезал бы все пути на строки в кавычках.

set _path="%PATH:;=" "%"
for %%p in (%_path%) do if not "%%~p"=="" echo %%~p

Итак, здесь отображаются ваши пути.

Команда FOR в cmd имеет утомительную кривую обучения, особенно потому, что переменные реагируют на операторы ()... вы можете назначать любые переменные, но вы не можете читать их обратно в (), если только вы не используете " setlocal ENABLEDELAYEDEXPANSION", и поэтому также используйте переменные с !! вместо %% (!_var!)

Я на данный момент исключительно скрипт с cmd, по работе пришлось всему этому учиться :)

person Jay    schedule 18.08.2009
comment
Добавление @ после do гарантирует, что команда не будет отображаться в командной строке. И если вы измените echo %%~p на echo.%%~p, вы можете удалить часть if not. Следовательно, вторую команду можно сократить до for %%p in (%_path%) do @echo.%%~p - person trlkly; 22.08.2014
comment
Вы правы, но @ должен быть полезен только в командной строке, так как скрипт должен начинаться с отключенного @echo. Мой цикл, использующий два %%, является признаком того, что он задуман как сценарий. Я никогда не думал об использовании эха. но только для пустой строки; однако в текущем сценарии это будет эхо пустой строки, чего я не хочу. Проголосовал за идею. - person Jay; 25.08.2014

Вы правильно поняли, но for /f предназначен для работы с многострочными файлами или командами, а не с отдельными строками.

В своей простейшей форме for похоже на for в Perl или foreach в любом другом языке. Вы передаете ему список токенов, и он перебирает их, каждый раз вызывая одну и ту же команду.

for %a in (hello world) do @echo %a

Расширения просто предоставляют автоматические способы построения списка токенов. Причина, по которой ваш текущий код ничего не дает, заключается в том, что ';' является символом конца строки (комментария) по умолчанию. Но даже если вы измените это, вам придется использовать %%g, %%h, %%i, ... для доступа к отдельным токенам, что сильно ограничит ваш командный файл.

Самое близкое, что вы можете получить к тому, что вы просите, это:

set TabbedPath=%PATH:;= %
for %%g in (%TabbedPath%) do echo %%g

Но это не удастся для путей в кавычках, содержащих точки с запятой.

По моему опыту, for /l и for /r хороши для расширения существующих команд, но в остальном for крайне ограничены. Вы можете сделать его немного более мощным (и запутанным) с помощью отложенного раскрытия переменных (cmd /v:on), но на самом деле это хорошо только для списков имен файлов.

Я бы предложил использовать WSH или PowerShell, если вам нужно выполнять манипуляции со строками. Если вы пытаетесь написать whereis для Windows, попробуйте where /?.

person Mark    schedule 03.05.2009
comment
Возможно, я слишком многого жду от командного файла. Спасибо за ответ. Меня могут не интересовать WSH или PowerShell, так как я уже использую Python. - person Sake; 04.05.2009
comment
Я придумал решение, основанное на вашем руководстве. Смотрите мой ответ ниже - person Sake; 04.05.2009
comment
Вы говорите '[...] for /f [...] не отдельные строки'. Мой сегодняшний пример показывает, как вы можете анализировать отдельную строку, если внутри нее есть используемые разделители. - person Kurt Pfeifle; 08.08.2010
comment
pipitas: конечно, это то, к чему привел мой бит TabbedPath. Но вы не можете полагаться на наличие пригодных для использования разделителей. - person Mark; 09.08.2010
comment
В своей простейшей форме for подобен for в Perl или foreach в любом другом языке. Вы передаете ему список токенов, и он перебирает их, каждый раз вызывая одну и ту же команду. Вы уверены? Похоже, он не повторяется. для /F токенов=1,2,3,4 разделителей=. %f в (127.0.0.1) do (echo asdf %f %g %h %i) asdf выводится только один раз. Так что это не похоже на foreach - person barlop; 22.09.2011
comment
/f не самая простая форма. Вы правы, с /f он перебирает строки, а не токены. - person Mark; 25.09.2011

Не мог удержаться от того, чтобы выбросить это туда, старое, как этот поток... Обычно, когда возникает необходимость перебрать каждый из файлов в PATH, все, что вы действительно хотите сделать, это найти конкретный файл... Если это так , этот однострочник выдаст первый каталог, в котором он найдет ваш файл:

(например: поиск java.exe)

for %%x in (java.exe) do echo %%~dp$PATH:x
person Tikonu    schedule 24.02.2013

Я знаю, что это СУПЕР старое... но просто для удовольствия я решил попробовать:

@Echo OFF
setlocal
set testpath=%path: =#%
FOR /F "tokens=* delims=;" %%P in ("%testpath%") do call :loop %%P
:loop
if '%1'=='' goto endloop
set testpath=%1
set testpath=%testpath:#= %
echo %testpath%
SHIFT
goto :loop
:endloop
pause
endlocal
exit

Это не требует подсчета и будет продолжаться до тех пор, пока не закончится. У меня была такая же проблема с пробелами, но она прошла через всю переменную. Ключом к этому являются метки циклов и функция SHIFT.

person iesou    schedule 01.05.2012

for /f выполняет итерации для каждой строки ввода, поэтому в вашей программе будет выводиться только первый путь.

ваша программа обрабатывает %PATH% как однострочный ввод и отделяет ;, помещает первый результат в %%g, затем выводит %%g (первый разделенный путь).

person Francis    schedule 03.05.2009
comment
Однако обратите внимание, что это не удастся, если в первом пути есть точка с запятой. Токенизация, выполненная for в этом случае, не будет обрабатывать цитирование. - person Joey; 03.05.2009

FOR по существу перебирает «линии» в наборе данных. В этом случае есть одна строка, содержащая путь. "delims=;" просто говорит ему разделять точки с запятой. Если вы измените тело на echo %%g,%%h,%%i,%%j,%%k, вы увидите, что он обрабатывает ввод как одну строку и разбивает ее на несколько токенов.

person D.Shawley    schedule 03.05.2009

Это работает для меня:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
@REM insure path is terminated with a ;
set tpath=%path%;
echo.
:again
@REM This FOR statement grabs the first element in the path
FOR /F "delims=;" %%I IN ("%TPATH%") DO (
  echo    %%I 
  @REM remove the current element of the path
  set TPATH=!TPATH:%%I;=!
)
@REM loop back if there is more to do. 
IF DEFINED TPATH GOTO :again

ENDLOCAL
person Cheeso    schedule 15.09.2009
comment
Орфографическая ошибка. Это гарантия, третья линия. - person barlop; 22.09.2010

Вы должны дополнительно использовать tokens=1,2,... часть опций, которые позволяет цикл for. Это здесь сделает то, что вы, возможно, хотите:

for /f "tokens=1,2,3,4,5,6,7,8,9,10,11,12 delims=;" %a in ("%PATH%") ^
do ( ^
     echo. %b ^
   & echo. %a ^
   & echo. %c ^
   & echo. %d ^
   & echo. %e ^
   & echo. %f ^
   & echo. %g ^
   & echo. %h ^
   & echo. %i ^
   & echo. %j ^
   & echo. %k ^
   & echo. ^
   & echo.   ...and now for some more... ^
   & echo. ^
   & echo. %a ^| %b ___ %c ... %d ^
   & dir "%e" ^
   & cd "%f" ^
   & dir /tw "%g" ^
   & echo. "%h  %i  %j  %k" ^
   & cacls "%f")

В этом примере обрабатываются только первые 12 токенов (=каталоги из %path%). Он использует явное перечисление каждого из используемых токенов. Обратите внимание, что имена токенов чувствительны к регистру: %a отличается от %A.

Чтобы избежать путей с пробелами, заключите все %x в кавычки, подобные этому "%i". Я не делал этого здесь, где я только повторяю токены.

Вы также можете сделать что-н. так:

for /f "tokens=1,3,5,7-26* delims=;" %a in ("%PATH%") ^
do ( ^
     echo. %c ^
   & echo. %b ^
   & echo. %a ^
   & echo. %d ^
   & echo. %e ^
   & echo. %f ^
   & echo. %g ^
   & echo. %h ^
   & echo. %i ^
   & echo. %j ^
   & echo. %k )

Он пропускает токены 2,4,6 и использует небольшое сокращение ("7-26") для обозначения остальных. Обратите внимание, как на этот раз %c, %b, %a обрабатываются в обратном порядке и как теперь они «означают» разные токены по сравнению с первым примером.

Так что это определенно не то краткое объяснение, которое вы просили. Но, возможно, примеры помогут прояснить ситуацию немного лучше...

person Kurt Pfeifle    schedule 07.08.2010
comment
Спасибо за дополнительные пояснения. Честно говоря, спустя год я уже почти не понимаю батник. Интересно, какой смысл в цикле for, если нам нужно работать с разными переменными для значений в одной и той же серии? - person Sake; 08.08.2010
comment
@Sake: цикл for в этом контексте разбивает ввод (%PATH%) на разные строки. Токены разбивают каждую строку на разные куски (границы зависят от заданных разделителей). Теперь в нашем случае у нас есть только 1 единственная строка ввода. (Но цикл одинаково хорошо будет работать с несколькими строками.) Мы используем цикл for, потому что у нас нет других средств для разделения строк (будь то одна или несколько строк) на фрагменты. - person Kurt Pfeifle; 17.08.2010
comment
этот пример несколько длинный. вы могли бы продемонстрировать это на более коротком примере, например, одной строкой. а не пример, когда можно заснуть, набрав его, затем проснуться, напечатать еще, поужинать, сделать опечатку в одной строке, разозлиться, начать снова и выйти. - person barlop; 20.09.2011
comment
@barlop: Я думаю, что ответ был как раз подходящей длины для человека, знающего дюжину языков программирования и говорящего, что он уже потратил 20 лет напрасно, чтобы понять это.... ;-P - person Kurt Pfeifle; 21.09.2011
comment
@barlop: ... кроме того, если поместить его в одну строку, это сделает его гораздо менее читаемым. Я рад, что ты так легко засыпаешь, несмотря на то, что мир такой злой. - person Kurt Pfeifle; 21.09.2011
comment
@pipitas за иллюстрацию концепции, т.е. с примером, это может быть короткая строка. В реализации он хочет работать с %PATH%, чтобы он был длинным, но в примере, иллюстрирующем концепцию с циклом for, не обязательно использовать %PATH%. - person barlop; 21.09.2011
comment
@Sake Интересно, в чем смысл цикла for, если нам нужно работать с разными переменными для значений в одной серии? Помимо итерации, вы должны использовать разные переменные Dir1 Dir2 Dir3 (и можно в цикле Dir%i ) и т.д. или, альтернативно, цикл через %f %g %h %i Поскольку пакетные файлы не включают структуру массива. И цикл по-прежнему работает для перебора всех этих переменных. Или как вы делали, 2 петли одна вар. один цикл просто обрабатывает первый элемент и удаляет первый элемент, а внешний цикл повторяет внутренний цикл до тех пор, пока строка не станет пустой или останется одна. - person barlop; 21.09.2011
comment
@ Ради того, если нам придется работать с разными переменными для значений, конечно ... вы справились с этим, не делая этого! - person barlop; 21.09.2011
comment
Чтобы исправить себя, это похоже на /f с токенами.. не повторяется... на самом деле, я имею в виду, что он сканирует (я не думаю, что кто-то сказал бы, что итерирует там) через строку, но не повторяет тело - person barlop; 22.09.2011

Вот хорошее руководство:

FOR /F — команда цикла: для набора файлов.

FOR /F — Команда цикла: против результатов другой команды.

FOR - Команда цикла: все параметры Файлы, Каталог, Список.

[Все руководство (команды Windows XP):

http://www.ss64.com/nt/index.html

Редактировать: Извините, я не заметил, что ссылка уже была в OP, так как мне она показалась частью ссылки Amazon.

person TFM    schedule 03.05.2009
comment
Я не понимаю /? help и поиск дополнительных объяснений или примеров. - person Sake; 04.05.2009

У меня работает, попробуй.

for /f "delims=;" %g in ('echo %PATH%') do echo %g%
person BerY    schedule 11.09.2015

У меня работает, попробуй.

for /f "токены=* разделители=;" %g in ('echo %PATH%') сделать echo %g%

person Silmar Veiga    schedule 06.04.2017
comment
Добро пожаловать в Stack Overflow! Хотя этот фрагмент кода приветствуется и может оказать некоторую помощь, он был бы значительно улучшен, если бы включал объяснение того, как и почему это решает проблему. Помните, что вы отвечаете на вопрос читателей в будущем, а не только того, кто задает сейчас! Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснение и указать, какие ограничения и предположения применяются. - person Toby Speight; 06.04.2017