windows 循环批处理文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6779533/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Batch file for loop
提问by keepitreall89
I am writing a batch file to run on Windows server 2008 R2, it has a for
loop in it and no matter how many tutorials I read online and try, it won't run the for loop.
我正在编写一个批处理文件以在 Windows server 2008 R2 上运行,它有一个for
循环,无论我在线阅读多少教程并尝试,它都不会运行 for 循环。
echo "Starting blur detection..."
if not exist %root%\output mkdir output
set tempnum = dir root | find /i "file"
set num = tempnum-5
for /l %idx in (0, 1, num) do (
%root%\blurDetection.exe %root%\%img%__%idx%.tif %root%\output
echo "blurDetection" %idx " of " %num )
Windows powershell says "idx was unexpected at this time." Any suggestions?
Windows powershell 显示“此时 idx 出乎意料”。有什么建议?
EDIT: Actually I think this line is causing the problem, I don't think it is getting and integer value in return.
编辑:实际上我认为这一行是导致问题的原因,我不认为它正在获取和整数值作为回报。
set tempnum = dir root | find /i "file"
回答by 0xC0000022L
(added after initial post) Oh and I missed the biggest issue. Which is indeed this line:
(在初始帖子后添加)哦,我错过了最大的问题。这确实是这一行:
set tempnum = dir root | find /i "file"
You mean to capture the output of dir
, right? This can, AFAIK only be done inside for
, unless you can live with outputting into a file and using that as input later.
您的意思是捕获 的输出dir
,对吗?这可以,AFAIK 只能在内部完成for
,除非您可以忍受输出到文件中并稍后将其用作输入。
Syntax is in such a case:
这种情况下的语法是:
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
Note:those are notbackticks.
注意:那些不是反引号。
Best explanations for NT scripting for
: http://www.robvanderwoude.com/ntfor.php
NT 脚本的最佳解释for
:http: //www.robvanderwoude.com/ntfor.php
One more note:since you appear to rely on the file name as some number, I suspect that dir /b
would be the better choice. Plain dir
will also output date and file size etc ...
还有一点要注意:由于您似乎依赖文件名作为某个数字,我怀疑这dir /b
将是更好的选择。平原dir
还将输出日期和文件大小等...
IIRC names of for
variables cannot have more than one letter. Also, inside script files (as apposed to the command line) those variables look like this:
for
变量的IIRC 名称不能超过一个字母。此外,在脚本文件中(与命令行相关),这些变量如下所示:
%%i
Besides, the
此外,该
set num=tempnum-5
should probably be
应该是
set /a num=%tempnum%-5
Another question about find
is whether you meant to use findstr
. Too little context, but findstr
seems more natural.
另一个问题find
是您是否打算使用 findstr
. 上下文太少,但findstr
似乎更自然。
From for /?
:
来自for /?
:
FOR %variable IN (set) DO command [command-parameters]
%variable Specifies a single letter replaceable parameter.
(set) Specifies a set of one or more files. Wildcards may be used.
command Specifies the command to carry out for each file.
command-parameters
Specifies parameters or switches for the specified command.
To use the FOR command in a batch program, specify %%variable instead
of %variable. Variable names are case sensitive, so %i is different
from %I.
Note in particular these two statements:
特别注意这两个语句:
%variable
Specifies a single letter replaceable parameter.- To use the
FOR
command in a batch program, specify%%variable
instead of%variable
.
%variable
指定单个字母可替换参数。- 要
FOR
在批处理程序中使用该命令,请指定%%variable
而不是%variable
。
Another peculiar feature of for loops is that you can split your input and tokens will get assigned to variables in alphabetical order. So inside a for %a ...
tokens would get assigned to %a
, %b
and so forth ...set tempnum = dir root | find /i "file"
for 循环的另一个特殊功能是您可以拆分输入,并且标记将按字母顺序分配给变量。所以在for %a ...
令牌内部将被分配给%a
,%b
依此类推...set tempnum = dir root | 找到 /i “文件”
回答by Joey
Ok, at first glance I can see fivesixseven (and a half) glaring errors in here:
好吧,我一眼就可以看到5 6七(半)在怒视着这里的错误:
1. Quotes around arguments to echo
.
1. 引述关于 的论据echo
。
echo
is a shell built-in and those had their syntax quite fixed 25 years ago in DOS and CP/M. echo
simply prints whatever comes after it until the end of the command (which could be things like a line break, )
, &
, |
, etc.), so what you want here is:
echo
是一个内置的 shell,它们的语法在 25 年前在 DOS 和 CP/M 中非常固定。echo
简单地后,无论发生什么事,直到命令结束打印(这可能是事情,比如换行符,)
,&
,|
,等),所以你想要的这里是:
echo Starting blur detection ...
and
和
echo blurDetection %i of %num%
2. Spaces around the equals sign in set
.
2. 等号周围的空格set
。
set
, like echo
is a shell built-in. Its syntax is also a bit awkward (although you have the same issue on some Unix shells as well). The point here is that everythingbetween set
and the equals sign is the variable name, everything after it is the value. In this case this means that your variable name ends with a spaceand the value starts with a space. Therefore, whatever you do, never put spaces around the equals sign. The language of the command processor is not very forgiving on whitespaces unlike many other programming languages. Therefore:
set
,就像echo
是一个内置的外壳。它的语法也有点笨拙(尽管您在某些 Unix shell 上也有同样的问题)。这里的重点是等号和等号之间的所有内容set
都是变量名,它之后的所有内容都是值。在这种情况下,这意味着您的变量名称以空格结尾,而值以空格开头。因此,无论您做什么,都不要在等号周围放置空格。与许多其他编程语言不同,命令处理器的语言对空格的容忍度不是很高。所以:
set foo=bar
and not
并不是
set foo = bar
otherwise you will never find a variable named foo
, because you'd need to use %foo %
(note the space in there).
否则,您将永远找不到名为 的变量foo
,因为您需要使用%foo %
(注意其中的空格)。
3. set
doesn't do arithmetic unless you tell it to.
3.set
不做算术,除非你告诉它。
set
does not calculate anything unless you explicitly tell it to. This is done with set /a
:
set
除非你明确告诉它,否则它不会计算任何东西。这是通过以下方式完成的set /a
:
The
/A
switch specifies that the string to the right of the equal sign is a numerical expression that is evaluated.
该
/A
开关指定等号右侧的字符串是要计算的数值表达式。
Therefore you need
因此你需要
set /a num=tempnum - 5
With /a
the %
around variable names can be omitted on the right side which makes the code a little clearer, usually.
随着/a
对%
周围的变量名可以在右侧,这使得代码更清晰一点,通常被省略。
4. set
only sets values, it doesn't execute any code.
4.set
只设置值,不执行任何代码。
In
在
set tempnum=dir root | find /i "file"
you apparently try to set a variable based on the outputof some command. This cannot work, as this essentially just sets tempnum
to dir root
and sends the output of that command (there is none) to find
because of the pipe. I'm not quite sure what this is supposed to do, but I'm guessing you're counting files.
您显然尝试根据某些命令的输出设置变量。这可不行,因为这本质上只是设置tempnum
到dir root
并发送(有没有)该命令的输出find
,因为管的。我不太确定这应该做什么,但我猜你在计算文件。
Generally, you shouldn't rely on locale-specific output of commands to accomplish something as this breaks fairly easily. Especially when you want to run batch files on other computers than your own (but even in that case you can run into problems). To count files you can iterate over the files and simply count how many you encounter. The for
statement can iterate over files in the following way:
通常,您不应该依赖特定于语言环境的命令输出来完成某些事情,因为这很容易中断。特别是当您想在自己的计算机以外的其他计算机上运行批处理文件时(但即使在这种情况下您也可能会遇到问题)。要计算文件,您可以遍历文件并简单地计算遇到的文件数量。该for
语句可以通过以下方式遍历文件:
set num=0
for %%f in (%root%\*) do set /a num+=1
Note that I'm assuming there that %root%
is the directory whose files you are interested in. Although you earlier used %root%
. If you're lucky, %root%
is just root
, otherwise your code never had a chance of working correctly.
请注意,我假设那里%root%
是您感兴趣的文件所在的目录。尽管您之前使用过%root%
. 如果你很幸运,%root%
只是root
,否则你的代码永远没有机会正常工作。
Since you apparently want to subtract 5 for some files that are not part of what you need, you can do
由于您显然想为一些不属于您需要的文件减去 5,您可以这样做
set /a num-=5
afterwards. Another option, if the files you're interested in share a common trait, would be to restrict the pattern to that, e.g.
然后。另一种选择,如果您感兴趣的文件共享一个共同特征,则将模式限制为该模式,例如
for %%f in (%root%\*.tif) do set /a num+=1
so that you don't need to subtract something from the final number.
这样你就不需要从最终数字中减去一些东西。
5. for
variables are single-letter only.
5.for
变量只有单字母。
The biggest one here, probably, which was pointed out by STATUS_ACCESS_DENIEDalready: The variables used in for
statements are special. They are not environment variables and they are only single-letter. The code therefore must read
这里最大的一个,可能是STATUS_ACCESS_DENIED已经指出的:for
语句中使用的变量是特殊的。它们不是环境变量,它们只是单个字母。因此代码必须阅读
for /l %%i in ...
6. for
variables must be prefixed by twopercent signs in batch files.
6.批处理文件中的for
变量必须以两个百分号为前缀。
On the command line a for
statement like
在命令行上,for
像这样的语句
for /l %x in (1,1,5) do @echo %x
is just fine, but in batch files the parser will stumble over that, probably expecting an argument to the program (which are %1
, %2
, etc.). As the documentation to for
already states, inside of batch files the variables need to use two %
:
是不错,但在批处理文件分析器会绊倒的是,可能期望的一个参数程序(这是%1
,%2
等)。正如文档for
已经指出的那样,在批处理文件中,变量需要使用两个%
:
for /l %%x in (1,1,5) do @echo %%x
This in turn will break down when used directly on the command line. TANSTAAFL.
当直接在命令行上使用时,这反过来会崩溃。坦斯塔夫。
However, this does not hold true for environment variables. No doubling of percent signs there (see also below).
但是,这不适用于环境变量。那里没有百分号加倍(另见下文)。
Also note that you have another error in the batch in the second-to-last line where you use idx
like an environment variable, even though you try using it as a for
variable elsewhere:
另请注意idx
,即使您尝试将其用作for
其他地方的变量,在倒数第二行的批处理中还有另一个错误,您在其中使用环境变量:
%root%\%img%__%idx%.tif
should read
应该读
%root%\%img%__%%i.tif
7. Environment variables must be surroundedby %
.
7.环境变量必须包围的%
。
The line
线
for /l %i in (0, 1, num) do ...
will never work and probably complain about num
being unexpected. To get the value of an environment variable you need to surround it with percent signs:
永远不会工作,可能会抱怨num
出乎意料。要获取环境变量的值,您需要用百分号将其括起来:
for /l %%i in (0, 1, %num%) do ...
otherwise it's just a string.
否则它只是一个字符串。
In a similar vein this also holds for the final echo
. %%i
is a for
variable, therefore only a prefix, but num
is an environment variable so it need to be surrounded by %
:
同样,这也适用于决赛echo
。%%i
是一个for
变量,因此只是一个前缀,但它num
是一个环境变量,所以它需要被包围%
:
echo blurDetection %%i of %num%
It can be confusing at times, but luckily we didn't yet look into delayed expansion :-).
有时可能会令人困惑,但幸运的是我们还没有研究延迟扩展:-)。
And a final one, though minor:
最后一个,虽然很小:
8. Always quote file names.
8. 总是引用文件名。
In your case the whole batch will break down as soon as %root%
contains a path with spaces. This starts in the second line where you look for a directory existing. Make that more robust by enclosing the path in spaces:
在您的情况下,一旦%root%
包含带空格的路径,整个批次就会分解。这从您查找现有目录的第二行开始。通过将路径包含在空格中来使其更加健壮:
if not exist "%root%\output" mkdir output
Same with the invocations inside the loop:
与循环内的调用相同:
"%root%\blurDetection.exe" "%root%\%img%__%%i.tif" "%root%\output"
You don't break anything when there are no spaces in the file name, but if there are it just works as expected.
当文件名中没有空格时,您不会破坏任何内容,但如果有,它就会按预期工作。
Now that we got that out of the way, we can look at how your batch looks now:
现在我们已经解决了这个问题,我们可以看看你的批次现在的样子:
echo Starting blur detection ...
if not exist "%root%\output" mkdir "%root%\output%"
set num=0
for %%f in (%root%\*.tif) do set /a num+=1
for /l %%i in (0, 1, %num%) do (
"%root%\blurDetection.exe" "%root%\%img%__%%i.tif" "%root%\output"
echo blurDetection %%i of %num%
)
This should at least work and is probably a fairly faithful rendition of what you thought of, which I'd summarize as:
这至少应该有效,并且可能是对您所想的相当忠实的演绎,我将其总结为:
- Create output directory if it doesn't yet exist
- Find the number of images to process (which are named sequentially)
- Process them and print status output.
- 如果尚不存在,则创建输出目录
- 查找要处理的图像数量(按顺序命名)
- 处理它们并打印状态输出。
But we are not done yet. As you noticed in above explanations, for
can already iterate over files. With step 2 and 3 we are iterating over the set of images twice which isn't exactly needed. All it takes is a single loop and some adaptions:
但我们还没有完成。正如您在上面的解释中注意到的那样,for
已经可以遍历文件。在第 2 步和第 3 步中,我们对这组图像进行了两次迭代,这并不完全是必需的。只需要一个循环和一些调整:
for %%f in (%root%\%img%__*.tif) do "%root%\blurDetection.exe" "%%f" "%root%\output"
But now we're missing the status output. And the files no longer are processed in order (which may or may not be a problem; I'm assuming here it isn't). If you don't desperately need a “Processed file of ” message, then the following would do:
但是现在我们缺少状态输出。并且不再按顺序处理文件(这可能是也可能不是问题;我在这里假设不是)。如果您不迫切需要“Processed file of ”消息,则可以执行以下操作:
for %%f in (%root%\%img%__*.tif) do (
"%root%\blurDetection.exe" "%%f" "%root%\output"
echo blurDetection %%~nf
)
which just outputs the last file name processed, but that will still process images in the order of 1
, 10
, 11
, ..., 19
, 2
, 20
... so the number very likely does not accurately resemble progress. If you need a counter, then you need the loop from earlier again which counts the files. I think I leave this now as it is.
它只输出处理的最后一个文件名,但仍会按1
, 10
, 11
, ..., 19
, 2
, 20
...的顺序处理图像,因此该数字很可能与进度不完全相同。如果您需要一个计数器,那么您需要再次使用之前的循环来对文件进行计数。我想我现在保持原样。
A few things I found curious:
我发现一些奇怪的事情:
- Neither
root
norimg
are defined anywhere. I'm guessing this is just a snippet from a larger program, but to the reader it isn't nice to stumble over variables that have no visible initialization. - While
%root%
is used in plenty of places, the current working directory seems to be identical (line 2, where you check for%root%\output
existing and if not, create justoutput
. Probably apushd
in the beginning and apopd
in the end would be a cleaner way of handling this. - You claim to quote a PowerShell error message, but that's a
cmd
error message. If you ran this script through PowerShell it'd die on the second line already.
- 也
root
没有img
在任何地方定义。我猜这只是一个较大程序的片段,但对读者来说,偶然发现没有可见初始化的变量并不好。 - 虽然
%root%
在很多地方使用,当前的工作目录似乎是相同的(2号线,在您检查%root%\output
现有的,如果没有,只创建output
。可能是一个pushd
在一开始和popd
最终将处理这种更清洁的方式. - 您声称引用了 PowerShell 错误消息,但这是一条
cmd
错误消息。如果你通过 PowerShell 运行这个脚本,它已经在第二行死了。
Another thing, since you mentioned this should run on Server 2008 R2: PowerShell is installed there by default and if your execution policy is set appropriately, you can also do this with a PowerShell script:
另一件事,因为您提到这应该在 Server 2008 R2 上运行:PowerShell 默认安装在那里,如果您的执行策略设置适当,您也可以使用 PowerShell 脚本执行此操作:
Write-Host Starting blur detection ...
if (!(Test-Path $root\output)) { New-Item -ItemType Directory $root\output }
$files = Get-ChildItem $root\*.tif
$files | ForEach-Object { $i = 1 } {
& "$root\blurDetection.exe" $_ $root\output
Write-Host blurDetection ($i++) of ($files.Count)
}