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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 17:32:26  来源:igfitidea点击:

Batch file for loop

windowspowershellbatch-file

提问by keepitreall89

I am writing a batch file to run on Windows server 2008 R2, it has a forloop 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 脚本的最佳解释forhttp: //www.robvanderwoude.com/ntfor.php

One more note:since you appear to rely on the file name as some number, I suspect that dir /bwould be the better choice. Plain dirwill also output date and file size etc ...

还有一点要注意:由于您似乎依赖文件名作为某个数字,我怀疑这dir /b将是更好的选择。平原dir还将输出日期和文件大小等...



IIRC names of forvariables 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 findis whether you meant to use findstr. Too little context, but findstrseems 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:

特别注意这两个语句:

  • %variableSpecifies a single letter replaceable parameter.
  • To use the FORcommand in a batch program, specify %%variableinstead 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, %band 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

echois a shell built-in and those had their syntax quite fixed 25 years ago in DOS and CP/M. echosimply 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 echois 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 setand 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. setdoesn't do arithmetic unless you tell it to.

3.set不做算术,除非你告诉它。

setdoes not calculate anything unless you explicitly tell it to. This is done with set /a:

set除非你明确告诉它,否则它不会计算任何东西。这是通过以下方式完成的set /a

The /Aswitch 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 /athe %around variable names can be omitted on the right side which makes the code a little clearer, usually.

随着/a%周围的变量名可以在右侧,这使得代码更清晰一点,通常被省略。

4. setonly 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 tempnumto dir rootand sends the output of that command (there is none) to findbecause of the pipe. I'm not quite sure what this is supposed to do, but I'm guessing you're counting files.

您显然尝试根据某些命令的输出设置变量。这可不行,因为这本质上只是设置tempnumdir 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 forstatement 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. forvariables are single-letter only.

5.for变量只有单字母。

The biggest one here, probably, which was pointed out by STATUS_ACCESS_DENIEDalready: The variables used in forstatements 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. forvariables must be prefixed by twopercent signs in batch files.

6.批处理文件中的for变量必须以两个百分号为前缀。

On the command line a forstatement 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 foralready 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 idxlike an environment variable, even though you try using it as a forvariable 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 numbeing 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. %%iis a forvariable, therefore only a prefix, but numis 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:

这至少应该有效,并且可能是对您所想的相当忠实的演绎,我将其总结为:

  1. Create output directory if it doesn't yet exist
  2. Find the number of images to process (which are named sequentially)
  3. Process them and print status output.
  1. 如果尚不存在,则创建输出目录
  2. 查找要处理的图像数量(按顺序命名)
  3. 处理它们并打印状态输出。

But we are not done yet. As you noticed in above explanations, forcan 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 rootnor imgare 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%\outputexisting and if not, create just output. Probably a pushdin the beginning and a popdin the end would be a cleaner way of handling this.
  • You claim to quote a PowerShell error message, but that's a cmderror 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)
}