“FOR”如何在cmd批处理文件中工作?

时间:2022-04-16 06:25:52

I've been programming in dozens of languages for 20 years but I could never understand how "FOR" work in windows cmd shell batch file, no matter how hard I tried. I read

我已经用几十种语言编程了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.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

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

and several other articles on the internet but still confuse and cannot accomplish anything.

还有其他一些关于互联网的文章,但仍然很混乱,不能完成任何事情。

Can anybody give me a concise explaination on how "FOR" works in general ?

谁能给我一个关于“FOR”一般工作的简明解释?

For a little more specific question, how can I iterate through each path in %PATH% variable ? I've tried with

对于更具体的问题,如何遍历% path %变量中的每个路径?我试过了,

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

That'd show only the first path, not all of them. Why ? What I do wrong ?

这只会显示第一条路径,而不是所有路径。为什么?我做错了什么?

12 个解决方案

#1


12  

None of the answers actually work. I've managed to find the solution myself. This is a bit hackish, but it solve the problem for me:

所有的答案都不奏效。我自己也找到了解决办法。这有点乱,但它解决了我的问题:

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
  )
)

Oh ! I hate batch file programming !!

哦!我讨厌批文件编程!!

Updated

更新

Mark's solution is simpler but it won't work with path containing whitespace. This is a little-modified version of Mark's solution

Mark的解决方案更简单,但是它不会使用包含空格的路径。这是Mark解决方案的一个很少修改的版本。

echo off
setlocal enabledelayedexpansion
set NonBlankPath=%PATH: =#%
set TabbedPath=%NonBlankPath:;= %
for %%g in (%TabbedPath%) do (
  set GG=%%g
  echo !GG:#= !
)

#2


5  

Mark's idea was good, but maybe forgot some path have spaces in them. Replacing ';' with '" "' instead would cut all paths into quoted strings.

马克的想法很好,但也许他忘了一些路径中有空格。用“”代替“,”改为“”,而是将所有路径切割成引用的字符串。

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

So here, you have your paths displayed.

这里显示了路径。

FOR command in cmd has a tedious learning curve, notably because how variables react within ()'s statements... you can assign any variables, but you can't read then back within the ()'s, unless you use the "setlocal ENABLEDELAYEDEXPANSION" statement, and therefore also use the variables with !!'s instead of %%'s (!_var!)

cmd中的命令有一个冗长的学习曲线,值得注意的是,变量如何在()语句中发生反应……您可以分配任何变量,但是您不能在()的范围内读取,除非您使用“setlocal ENABLEDELAYEDEXPANSION”语句,因此也可以使用变量!!而不是%'s (!_var!)

I currently exclusively script with cmd, for work, had to learn all this :)

我目前只负责cmd的脚本,为了工作,必须学习所有这些:)

#3


4  

You've got the right idea, but for /f is designed to work on multi-line files or commands, not individual strings.

您有正确的想法,但是for /f被设计用于处理多行文件或命令,而不是单独的字符串。

In its simplest form, for is like Perl's for, or every other language's foreach. You pass it a list of tokens, and it iterates over them, calling the same command each time.

在最简单的形式中,for就像Perl的for,或者其他语言的foreach。您传递一个令牌列表,并遍历它们,每次调用相同的命令。

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

The extensions merely provide automatic ways of building the list of tokens. The reason your current code is coming up with nothing is that ';' is the default end of line (comment) symbol. But even if you change that, you'd have to use %%g, %%h, %%i, ... to access the individual tokens, which will severely limit your batch file.

扩展仅仅提供了构建令牌列表的自动方式。您当前的代码没有出现任何问题的原因是“;”是行(注释)符号的默认端点。但是即使你改变了,你也要使用%%g %% %%i,…访问单个令牌,这将严重限制您的批处理文件。

The closest you can get to what you ask for is:

你所要求的最接近的是:

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

But that will fail for quoted paths that contain semicolons.

但是,如果引用的路径包含分号,则会失败。

In my experience, for /l and for /r are good for extending existing commands, but otherwise for is extremely limited. You can make it slightly more powerful (and confusing) with delayed variable expansion (cmd /v:on), but it's really only good for lists of filenames.

在我的经验中,for /l和for /r对于扩展现有的命令是有好处的,但除此之外,它是非常有限的。您可以使用延迟变量扩展(cmd /v:on)使其更强大(和混乱),但它实际上只对文件名列表有好处。

I'd suggest using WSH or PowerShell if you need to perform string manipulation. If you're trying to write whereis for Windows, try where /?.

如果需要执行字符串操作,我建议使用WSH或PowerShell。如果你想为Windows写什么,试试在哪里?

#4


4  

Couldn't resist throwing this out there, old as this thread is... Usually when the need arises to iterate through each of the files in PATH, all you really want to do is find a particular file... If that's the case, this one-liner will spit out the first directory it finds your file in:

我忍不住把这个扔了出去,因为这条线是……通常,当需要遍历路径中的每个文件时,您真正想做的就是找到一个特定的文件…如果是这样的话,这个一行程序将会吐出它找到你文件的第一个目录:

(ex: looking for java.exe)

(例:寻找java.exe)

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

#5


3  

I know this is SUPER old... but just for fun I decided to give this a try:

我知道这很古老……但为了好玩,我决定试一试:

@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

This doesn't require a count and will go until it finishes. I had the same problem with spaces but it made it through the entire variable. The key to this is the loop labels and the SHIFT function.

这并不需要计数,并且会一直持续到完成为止。我对空间有同样的问题,但它通过整个变量。这其中的关键是循环标签和移位功能。

#6


2  

for /f iterates per line input, so in your program will only output first path.

for /f迭代每个行输入,因此在您的程序中只会输出第一个路径。

your program treats %PATH% as one-line input, and deliminate by ;, put first result to %%g, then output %%g (first deliminated path).

您的程序将%PATH%作为一行输入,并通过;,将第一个结果放在%g上,然后输出%%g(第一个deliminated路径)。

#7


2  

FOR is essentially iterating over the "lines" in the data set. In this case, there is one line that contains the path. The "delims=;" is just telling it to separate on semi-colons. If you change the body to echo %%g,%%h,%%i,%%j,%%k you'll see that it is treating the input as a single line and breaking it into multiple tokens.

FOR本质上是遍历数据集中的“行”,在本例中,有一行包含路径。“delims=”只是告诉它在分号上分开。如果您将主体更改为echo %%g,%% %% %% %% %% %% %% %k,您将看到它将输入视为一行,并将其拆分为多个令牌。

#8


2  

This works for me:

这工作对我来说:

@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

#9


1  

Here is a good guide:

这里有一个很好的指南:

FOR /F - Loop command: against a set of files.

FOR /F - Loop命令:针对一组文件。

FOR /F - Loop command: against the results of another command.

FOR /F - Loop命令:针对另一个命令的结果。

FOR - Loop command: all options Files, Directory, List.

FOR - Loop命令:所有选项文件、目录、列表。

[The whole guide (Windows XP commands):

[整个指南(Windows XP命令):

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

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

Edit: Sorry, didn't see that the link was already in the OP, as it appeared to me as a part of the Amazon link.

编辑:对不起,没有看到链接已经在OP中,因为它在我看来是亚马逊链接的一部分。

#10


1  

You have to additionally use the tokens=1,2,... part of the options that the for loop allows. This here will do what you possibly want:

您必须另外使用令牌=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")

This example processes the first 12 tokens (=directories from %path%) only. It uses explicit enumeration of each of the used tokens. Note, that the token names are case sensitive: %a is different from %A.

这个示例只处理前12个令牌(=目录%path%)。它使用了每个使用的标记的显式枚举。注意,令牌名称是区分大小写的:%a与%a不同。

To be save for paths with spaces, surround all %x with quotes like this "%i". I didn't do it here where I'm only echoing the tokens.

要为带有空格的路径保存,请使用“%i”这样的引号包围所有%x。我没有在这里做,我只是在重复符号。

You could also do s.th. like this:

你也可以这样做。是这样的:

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 )

This one skips tokens 2,4,6 and uses a little shortcut ("7-26") to name the rest of them. Note how %c, %b, %a are processed in reverse order this time, and how they now 'mean' different tokens, compared to the first example.

这一段跳过了标记2、4、6,并使用了一个小的快捷方式(“7-26”)来命名其余部分。请注意,与第一个示例相比,这一次,%c、%b、%a是如何以相反的顺序处理的,以及它们现在是如何“表示”不同的标记的。

So this surely isn't the concise explanation you asked for. But maybe the examples help to clarify a little better now...

所以这肯定不是你要求的简洁的解释。但也许这些例子有助于澄清一点……

#11


0  

It works for me, try it.

对我来说很有用,试试吧。

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

#12


-1  

It works for me, try it.

对我来说很有用,试试吧。

for /f "tokens=* delims=;" %g in ('echo %PATH%') do echo %g%

/ f”令牌= * delims =;”%g in ('echo %PATH%')回波%g%。

#1


12  

None of the answers actually work. I've managed to find the solution myself. This is a bit hackish, but it solve the problem for me:

所有的答案都不奏效。我自己也找到了解决办法。这有点乱,但它解决了我的问题:

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
  )
)

Oh ! I hate batch file programming !!

哦!我讨厌批文件编程!!

Updated

更新

Mark's solution is simpler but it won't work with path containing whitespace. This is a little-modified version of Mark's solution

Mark的解决方案更简单,但是它不会使用包含空格的路径。这是Mark解决方案的一个很少修改的版本。

echo off
setlocal enabledelayedexpansion
set NonBlankPath=%PATH: =#%
set TabbedPath=%NonBlankPath:;= %
for %%g in (%TabbedPath%) do (
  set GG=%%g
  echo !GG:#= !
)

#2


5  

Mark's idea was good, but maybe forgot some path have spaces in them. Replacing ';' with '" "' instead would cut all paths into quoted strings.

马克的想法很好,但也许他忘了一些路径中有空格。用“”代替“,”改为“”,而是将所有路径切割成引用的字符串。

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

So here, you have your paths displayed.

这里显示了路径。

FOR command in cmd has a tedious learning curve, notably because how variables react within ()'s statements... you can assign any variables, but you can't read then back within the ()'s, unless you use the "setlocal ENABLEDELAYEDEXPANSION" statement, and therefore also use the variables with !!'s instead of %%'s (!_var!)

cmd中的命令有一个冗长的学习曲线,值得注意的是,变量如何在()语句中发生反应……您可以分配任何变量,但是您不能在()的范围内读取,除非您使用“setlocal ENABLEDELAYEDEXPANSION”语句,因此也可以使用变量!!而不是%'s (!_var!)

I currently exclusively script with cmd, for work, had to learn all this :)

我目前只负责cmd的脚本,为了工作,必须学习所有这些:)

#3


4  

You've got the right idea, but for /f is designed to work on multi-line files or commands, not individual strings.

您有正确的想法,但是for /f被设计用于处理多行文件或命令,而不是单独的字符串。

In its simplest form, for is like Perl's for, or every other language's foreach. You pass it a list of tokens, and it iterates over them, calling the same command each time.

在最简单的形式中,for就像Perl的for,或者其他语言的foreach。您传递一个令牌列表,并遍历它们,每次调用相同的命令。

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

The extensions merely provide automatic ways of building the list of tokens. The reason your current code is coming up with nothing is that ';' is the default end of line (comment) symbol. But even if you change that, you'd have to use %%g, %%h, %%i, ... to access the individual tokens, which will severely limit your batch file.

扩展仅仅提供了构建令牌列表的自动方式。您当前的代码没有出现任何问题的原因是“;”是行(注释)符号的默认端点。但是即使你改变了,你也要使用%%g %% %%i,…访问单个令牌,这将严重限制您的批处理文件。

The closest you can get to what you ask for is:

你所要求的最接近的是:

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

But that will fail for quoted paths that contain semicolons.

但是,如果引用的路径包含分号,则会失败。

In my experience, for /l and for /r are good for extending existing commands, but otherwise for is extremely limited. You can make it slightly more powerful (and confusing) with delayed variable expansion (cmd /v:on), but it's really only good for lists of filenames.

在我的经验中,for /l和for /r对于扩展现有的命令是有好处的,但除此之外,它是非常有限的。您可以使用延迟变量扩展(cmd /v:on)使其更强大(和混乱),但它实际上只对文件名列表有好处。

I'd suggest using WSH or PowerShell if you need to perform string manipulation. If you're trying to write whereis for Windows, try where /?.

如果需要执行字符串操作,我建议使用WSH或PowerShell。如果你想为Windows写什么,试试在哪里?

#4


4  

Couldn't resist throwing this out there, old as this thread is... Usually when the need arises to iterate through each of the files in PATH, all you really want to do is find a particular file... If that's the case, this one-liner will spit out the first directory it finds your file in:

我忍不住把这个扔了出去,因为这条线是……通常,当需要遍历路径中的每个文件时,您真正想做的就是找到一个特定的文件…如果是这样的话,这个一行程序将会吐出它找到你文件的第一个目录:

(ex: looking for java.exe)

(例:寻找java.exe)

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

#5


3  

I know this is SUPER old... but just for fun I decided to give this a try:

我知道这很古老……但为了好玩,我决定试一试:

@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

This doesn't require a count and will go until it finishes. I had the same problem with spaces but it made it through the entire variable. The key to this is the loop labels and the SHIFT function.

这并不需要计数,并且会一直持续到完成为止。我对空间有同样的问题,但它通过整个变量。这其中的关键是循环标签和移位功能。

#6


2  

for /f iterates per line input, so in your program will only output first path.

for /f迭代每个行输入,因此在您的程序中只会输出第一个路径。

your program treats %PATH% as one-line input, and deliminate by ;, put first result to %%g, then output %%g (first deliminated path).

您的程序将%PATH%作为一行输入,并通过;,将第一个结果放在%g上,然后输出%%g(第一个deliminated路径)。

#7


2  

FOR is essentially iterating over the "lines" in the data set. In this case, there is one line that contains the path. The "delims=;" is just telling it to separate on semi-colons. If you change the body to echo %%g,%%h,%%i,%%j,%%k you'll see that it is treating the input as a single line and breaking it into multiple tokens.

FOR本质上是遍历数据集中的“行”,在本例中,有一行包含路径。“delims=”只是告诉它在分号上分开。如果您将主体更改为echo %%g,%% %% %% %% %% %% %% %k,您将看到它将输入视为一行,并将其拆分为多个令牌。

#8


2  

This works for me:

这工作对我来说:

@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

#9


1  

Here is a good guide:

这里有一个很好的指南:

FOR /F - Loop command: against a set of files.

FOR /F - Loop命令:针对一组文件。

FOR /F - Loop command: against the results of another command.

FOR /F - Loop命令:针对另一个命令的结果。

FOR - Loop command: all options Files, Directory, List.

FOR - Loop命令:所有选项文件、目录、列表。

[The whole guide (Windows XP commands):

[整个指南(Windows XP命令):

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

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

Edit: Sorry, didn't see that the link was already in the OP, as it appeared to me as a part of the Amazon link.

编辑:对不起,没有看到链接已经在OP中,因为它在我看来是亚马逊链接的一部分。

#10


1  

You have to additionally use the tokens=1,2,... part of the options that the for loop allows. This here will do what you possibly want:

您必须另外使用令牌=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")

This example processes the first 12 tokens (=directories from %path%) only. It uses explicit enumeration of each of the used tokens. Note, that the token names are case sensitive: %a is different from %A.

这个示例只处理前12个令牌(=目录%path%)。它使用了每个使用的标记的显式枚举。注意,令牌名称是区分大小写的:%a与%a不同。

To be save for paths with spaces, surround all %x with quotes like this "%i". I didn't do it here where I'm only echoing the tokens.

要为带有空格的路径保存,请使用“%i”这样的引号包围所有%x。我没有在这里做,我只是在重复符号。

You could also do s.th. like this:

你也可以这样做。是这样的:

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 )

This one skips tokens 2,4,6 and uses a little shortcut ("7-26") to name the rest of them. Note how %c, %b, %a are processed in reverse order this time, and how they now 'mean' different tokens, compared to the first example.

这一段跳过了标记2、4、6,并使用了一个小的快捷方式(“7-26”)来命名其余部分。请注意,与第一个示例相比,这一次,%c、%b、%a是如何以相反的顺序处理的,以及它们现在是如何“表示”不同的标记的。

So this surely isn't the concise explanation you asked for. But maybe the examples help to clarify a little better now...

所以这肯定不是你要求的简洁的解释。但也许这些例子有助于澄清一点……

#11


0  

It works for me, try it.

对我来说很有用,试试吧。

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

#12


-1  

It works for me, try it.

对我来说很有用,试试吧。

for /f "tokens=* delims=;" %g in ('echo %PATH%') do echo %g%

/ f”令牌= * delims =;”%g in ('echo %PATH%')回波%g%。