Как работает «FOR» в командном файле cmd? - PullRequest
23 голосов
/ 03 мая 2009

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

http://www.amazon.com/Windows-Administration-Command-Line-Vista/dp/0470046163/ref=sr_1_1?ie=UTF8&s=books&qid=1241362727&sr=8-1

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

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

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

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

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

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

Ответы [ 12 ]

12 голосов
/ 04 мая 2009

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

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:#= !
)
5 голосов
/ 18 августа 2009

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

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

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

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

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

4 голосов
/ 24 февраля 2013

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

(например: ищу java.exe)

for %%x in (java.exe) do echo %%~dp$PATH:x
4 голосов
/ 03 мая 2009

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

В простейшей форме for подобен Perl for или любому другому языку 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 /?.

3 голосов
/ 01 мая 2012

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

@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.

2 голосов
/ 08 августа 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.

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

Вы также можете сделать s.th. как это:

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 на этот раз обрабатываются в обратном порядке, и как они теперь «означают» разные токены по сравнению с первым примером.

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

2 голосов
/ 15 сентября 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
2 голосов
/ 03 мая 2009

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

2 голосов
/ 03 мая 2009

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

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

1 голос
/ 03 мая 2009

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

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...