批量退出for循环 [英] Exit a for loop in batch

查看:125
本文介绍了批量退出for循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果 j 达到 0,我不想为循环留下

  set / aj = 3 
for / R c:\dsd_imports\\ (*。*)DO(
MDI_import_ad_command.bat C:\DSD_IMPORTS\AD_AND_DEAL\ %%〜nxi
MOVE %% ic:\dsd_imports\ad_and_deal\\ \\ in_process
set / aj = j-1
if j == 0
break


解决方案

这里是您的批处理代码重写和评论:

  @echo off 
rem定义环境变量FileCount的值为3.
设置FileCount = 3

rem在堆栈上推送当前目录的路径,
rem当前目录中的所有内容,直至命令POPD。
pushd C:\dsd_imports\ad_and_deal

rem在所有非隐藏文件上指定的目录中进行处理。

rem为每个文件调用另一个带有当前文件名称的批处理文件。
rem然后将当前文件移动到子目录in_process中,并将
rem文件计数变量减1。

rem启用延迟扩展,从而创建
副本把所有的环境变量都删除,再次将当前目录推到堆栈
rem。

rem运行字符串比较(比整数
rem比较快几个微秒,因为环境变量总是字符串类型)为
rem确定是否已经处理了3个文件这种情况下,循环
rem退出时跳转到循环下方的标签。

rem在任何情况下,必须使用命令
rem ENDLOCAL在批处理文件执行继续执行标签Done或
rem之前使用循环执行来恢复以前的环境。 (*)do(
)调用MDI_import_ad_command.bat%% I
move / Y%% Iin_process \


set / A FileCount- = 1
setlocal EnableDelayedExpansion
如果!FileCount!==0endlocal& goto完成
endlocal


rem删除不再需要的环境变量FileCount。
rem然后从堆栈中弹出以前的当前目录路径,并使
将该目录再次移动到批处理文件剩余的当前目录。

:完成
设置FileCount =
popd

我希望你不需要递归地处理 C:\dsd_imports\ad_and_deal 中的文件,因为这样会导致处理子目录中已经处理的文件 in_process



为了解所使用的命令及其工作方式,请打开命令提示符窗口,命令,并仔细阅读每个命令显示的所有帮助页面。




  • call /? $ li $ echo /?

  • endlocal /?

  • 转到/?

  • if /?

  • move /?
  • code> popd /?

  • pushd /?
  • rem /?

  • set /?
  • setlocal /?






额外信息a回合比较值与IF



IF 等于运算符 == 结果总是以字符串形式进行比较,而运算符 EQU 第一个总是尝试一个整数比较,如果不可能的话也执行一个字符串比较,因为它可以被证明如下:

  @echo off 
如果00 == 0(使用==时echo 00等于0)else(echo 00 is different 0使用==)
如果00 EQU 0(使用EQU时echo 00等于0)else(使用EQU时echo 00不等于0)

执行的输出是:

  00在使用= = 
00在使用EQU

时等于0在上面的双引号参数!FileCount! 0 可以被安全地移除,但并不总是这样,但是在这里。 / p>

我添加了双引号,让每个人都清楚字符串被比较,因为使用双引号将两个参数进行比较。


$ b $ <<> c == / strong>可以在 C 中编码,例如:

  #include< stdio.h> 
#include< string.h>
$ b $ main(int argc,char * args [])
{
if(argc!= 3)
{
puts(Error:这个比较演示只需要两个参数。);
返回2;

$ b $注意:在调用函数main之前执行的用于编译器的可执行文件
被添加的启动代码删除了最可能的
参数周围的双引号字符串。
以\value\形式指定参数,以将
参数与周围的双引号进行比较。 * /
printf(比较%s与%s.\\\
,args [1],args [2]);
$ b $ if(strcmp(args [1],args [2])== 0)
{
puts(The strings are equal。);
返回0;
}

puts(字符串不同);
返回1;

$ / code>

所以使用!FileCount! ==0!FileCount! == 0 strcmp 必须比较4与2字节,包括终止的空字节。这并没有真正的区别,因为通过修改上面的代码可以证明这一点,并且在一个循环中运行 strcmp 例如100.000.000次并且再次测量执行时间并在核心/处理器的缓存中再次进行比较。
$ b

SETLOCAL ENDLOCAL 因为这两个命令所做的所有操作(如 循环并不会影响批处理文件执行完成所需的时间。 / b / b / a / 38676582/3074564>这个答案

所以速度会更快:

<$
setlocal EnableExtensions EnableDelayedExpansion
setFileCount = 3
cd / DC:\dsd_imports\ad_and_deal
$ b (*)do(
call MDI_import_ad_command.bat%% I
move / Y%% Iin_process \
set / A FileCount- = 1
if!F ileCount!== 0转到完成


:完成
rem在这里添加其他命令。

rem此命令销毁环境变量的本地副本,
rem意味着如果在运行
rem此批处理文件之前不存在FileCount,则不会再存在。它还使用命令CD恢复以前的当前目录改变
rem。
endlocal

但是这个快速的批处理代码不起作用, FOR 包含一个或多个感叹号。原因是文件名中的第一个被解释为延迟的环境变量引用的开始,如果没有第二个被解释为延迟环境变量引用的结束,文件名中这两个之间的字符串将被替换 %% I 在调用另一个批处理文件之前。



这个总是不需要的行为可以通过运行这个批处理文件:

  @echo off 
echo File!1.txt>%TEMP%\File!1.txt
echo File!2!.txt>%TEMP%\File!2!.txt
echo File!XYZ! abc!.txt>%TEMP%\File!XYZ!abc!.txt

echo默认情况下禁用延迟扩展:
echo /

%for %% I in(%TEMP%\File *)do echo%%〜nxI

echo /
echo显式启用延迟扩展:
回声/

setlocal EnableExtensions EnableDelayedExpansion
为%%我在(%TEMP%\文件*)回声%%〜nxI
endlocal

del%TEMP%\File *> nul
echo /
pause

在Windows XP和Windows 7上执行的这个批处理文件的输出是:

 默认情况下:

File!1.txt
File!2!.txt
File!XYZ!abc!.txt

显式启用延迟扩展:

文件1.txt
文件.txt
文件abc.txt
EQU
的等价 C

  #include  
#include< stdlib.h>
#include< string.h>
$ b int main(int argc,char * args [])
{
char * psEnd;
long int lArgument1;
long int lArgument2;
$ b $ if(argc!= 3)
{
puts(错误:这个比较演示只需要两个参数。
返回2;

$ b $ *注意:在调用函数main之前执行的用于编译器的可执行文件
被添加的启动代码删除了最可能的
参数中的双引号字符串。
以\value\形式指定参数,以将
参数与周围的双引号进行比较。 * /
printf(%s EQU%s \ n,args [1],args [2]);

lArgument1 = strtol(args [1],& psEnd,0);
if(* psEnd!='\0')
{
if(strcmp(args [1],args [2])== 0)
{
puts(字符串相等);
返回0;
}
puts(字符串不同);
返回1;
}

lArgument2 = strtol(args [2],& psEnd,0);
if(* psEnd!='\0')
{
if(strcmp(args [1],args [2])== 0)
{
puts(字符串相等);
返回0;
}
puts(字符串不同);
返回1;


if(lArgument1 == lArgument2)
{
printf(整数%ld和%ld是相等的。\ n,lArgument1,lArgument2 );
返回0;
}
printf(整数%ld和%ld是不同的。\ n,lArgument1,lArgument2);
返回1;





$ b

比较这个C代码来演示 EQU 行为与上面的C代码演示 == 行为,使用 EQU 会导致执行更多的CPU指令,比使用 == 运算符进行字符串比较要多。在单步模式下运行应用程序也可以使用标准库函数 strcmp 和<更加清楚的是处理器必须执行更多的指令才能运行一个整数在批处理文件中进行比较比字符串比较。



C 编写的第二个应用程序显示了批处理文件编写者使用数字在批处理文件中包含一个或多个前导零,用于比较 EQU 的值或在算术表达式中使用它们,即在 set / A



例如,将上面的代码编译为 equ.exe 并运行以下命令:

  @echo off 
equ.exe \08 \\08 \
equ .exe 08 8
equ.exe 14 14
equ.exe 014 014
equ.exe 0x14 0x14 $ b $ equ.exe 0x14 20 $ b $ equ.exe 0x14 \20 \

使用gpp 4.7.3(DJGPP包)编译得到的结果是 equ.exe

 08EQU08
字符串相等。
08 EQU 8
字符串是不同的。
14 EQU 14
整数14和14相等。
014 EQU 014
整数12和12相等。
0x14 EQU 0x14
整数20和20相等。
0x14 EQU 20
整数20和20相等。
0x14 EQU20
字符串不同。

第一个比较08EQU08在这两个参数中都是作为字符串比较执行的。



第二个比较 08 EQU 8 最后也作为字符串执行,而不是整数比较,因为第一个参数以前导0开始,因此被函数 strtol 第三个参数 base 0 为八进制数,由于包含数字8,因此八进制数是无效的。因此,字符串到长整数的转换失败,因此,为 08 比较 8

第三个比较 14 EQU 14 被执行为整数比较,两个数字被解释为十进制。

第四个比较 014 EQU 014 也作为整数比较执行,但是两个数字都是interp第二个比较 0x14 EQU 0x14 被再次执行为整数比较,但是两个数字都被解释十六进制解释两次 20 作为输出数字。



因此,建议始终运行两个值的字符串比较批处理文件尽可能使用运算符 == 和不使用或使用明确围绕双引号。

无用的方法是使用批处理文件来测量 == EQU 之间的时间差异,因为Windows命令解释器解析所需的时间在执行 IF 条件之前,批处理文件中的命令行比内部使用的已编译的C / C ++代码显示的比较本身所需的时间长少许多。 / p>

当然这也意味着使用 == 或者 EQU 对用户rega没有真正的区别使用批处理文件完成任务所需的总时间。但是,除了执行时间之外,使用 == EQU 常常会有所不同。


I wan't to leave the for loop if the count j reaches 0.

set /a j=3
for /R c:\dsd_imports\ad_and_deal %%i IN (*.*) DO (
  MDI_import_ad_command.bat C:\DSD_IMPORTS\AD_AND_DEAL\%%~nxi
  MOVE %%i c:\dsd_imports\ad_and_deal\in_process
  set /a j=j-1
  if j == 0
     break
)

解决方案

Here is your batch code rewritten and commented:

@echo off
rem Define environment variable FileCount with value 3.
set "FileCount=3"

rem Push path of current directory on stack and make specified directory
rem the current directory for everything up to command POPD.
pushd C:\dsd_imports\ad_and_deal

rem Process in directory specified above all non hidden files.

rem For each file call another batch file with name of current file.
rem Then move current file to subdirectory in_process and decrement
rem the file count variable by 1.

rem Enable delayed expansion which results also in creating a copy of
rem all environment variables and pushing current directory once again
rem on stack.

rem Run a string comparison (a few microseconds faster than an integer
rem comparison as environment variables are always of type string) to
rem determine if 3 files were already processed in which case the loop
rem is exited with a jump to a label below the loop.

rem In any case the previous environment must be restored with command
rem ENDLOCAL before the batch file execution continues on label Done or
rem with loop execution.

for %%I in (*) do (
    call MDI_import_ad_command.bat "%%I"
    move /Y "%%I" in_process\
    set /A FileCount-=1
    setlocal EnableDelayedExpansion
    if "!FileCount!" == "0" endlocal & goto Done
    endlocal
)

rem Delete the environment variable FileCount as no longer needed.
rem Then pop the previous current directory path from stack and make
rem this directory again the current directory for rest of batch file.

:Done
set "FileCount="
popd

I hope you don't really need to recursively process files in C:\dsd_imports\ad_and_deal as this would result in processing also the files already processed in subdirectory in_process.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • echo /?
  • endlocal /?
  • goto /?
  • if /?
  • move /?
  • popd /?
  • pushd /?
  • rem /?
  • set /?
  • setlocal /?

Extra information about comparing values with IF

The IF equal operator == results always in a string comparison while operator EQU first always tries an integer comparison and performs also a string comparison if this is not possible as it can be proven with:

@echo off
if 00 == 0  (echo 00 is equal 0 on using ==)  else (echo 00 is different 0 on using ==)
if 00 EQU 0 (echo 00 is equal 0 on using EQU) else (echo 00 is different 0 on using EQU)

The output on execution is:

00 is different 0 on using ==
00 is equal 0 on using EQU

In the batch code above the double quotes around the arguments !FileCount! and 0 can be safely removed which is not always the case, but here it is.

I added the double quotes to make it clear for everybody that strings are compared because the double quotes of both arguments are compared as well.

The == operator of IF could be coded in C for example with this code:

#include <stdio.h>
#include <string.h>

int main(int argc, char* args[])
{
    if(argc != 3)
    {
        puts("Error: This compare demo requires exactly two parameters.");
        return 2;
    }

    /* Note: The startup code added by used compiler to executable being
             executed before calling function main removes most likely
             the surrounding double quotes on the argument strings.
             Specify the arguments in form \"value\" to compare
             the arguments with surrounding double quotes. */
    printf("Compare %s with %s.\n",args[1],args[2]);

    if(strcmp(args[1],args[2]) == 0)
    {
        puts("The strings are equal.");
        return 0;
    }

    puts("The strings are different.");
    return 1;
}

So the difference on using "!FileCount!" == "0" versus !FileCount! == 0 is that strcmp has to compare 4 versus 2 bytes including the terminating null byte. This does not make a real difference as it could be proven by modifying the code above and run strcmp for example 100.000.000 times in a loop and measure the execution time for this again and again done compare in caches of the core/processor.

The usage of SETLOCAL and ENDLOCAL within the FOR loop and not outside makes a difference in time required for batch file execution completion because of all the operations done by those two commands as described in bottom half of this answer.

So faster would be definitely:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FileCount=3"
cd /D C:\dsd_imports\ad_and_deal

for %%I in (*) do (
    call MDI_import_ad_command.bat "%%I"
    move /Y "%%I" in_process\
    set /A FileCount-=1
    if !FileCount! == 0 goto Done
)

:Done
rem Add here other commands.

rem This command destroys the local copy of the environment variables which
rem means FileCount does not exist anymore if it did not exist before running
rem this batch file. It also restores the previous current directory changed
rem above with command CD.
endlocal

But this faster batch code does not work if any file found by FOR contains 1 or more exclamation marks. The reason is that first ! in a file name is interpreted as begin of a delayed environment variable reference which is removed from file name if there is no second ! which is interpreted as end of delayed environment variable reference and the string between those two ! in file name would be replaced by most likely nothing on expanding %%I before calling the other batch file.

This always unwanted behavior can be seen by running this batch file:

@echo off
echo File !1.txt>"%TEMP%\File !1.txt"
echo File !2!.txt>"%TEMP%\File !2!.txt"
echo File !XYZ! abc!.txt>"%TEMP%\File !XYZ! abc!.txt"

echo With delayed expansion disabled as by default:
echo/

for %%I in ("%TEMP%\File *") do echo "%%~nxI"

echo/
echo With delayed expansion enabled explicitly:
echo/

setlocal EnableExtensions EnableDelayedExpansion
for %%I in ("%TEMP%\File *") do echo "%%~nxI"
endlocal

del "%TEMP%\File *" >nul
echo/
pause

The output of this batch file executed on Windows XP and Windows 7 is:

With delayed expansion disabled as by default:

"File !1.txt"
"File !2!.txt"
"File !XYZ! abc!.txt"

With delayed expansion enabled explicitly:

"File 1.txt"
"File .txt"
"File  abc.txt"

For completeness the equivalent C code for operator EQU:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* args[])
{
    char* psEnd;
    long int lArgument1;
    long int lArgument2;

    if(argc != 3)
    {
        puts("Error: This compare demo requires exactly two parameters.");
        return 2;
    }

    /* Note: The startup code added by used compiler to executable being
             executed before calling function main removes most likely
             the surrounding double quotes on the argument strings.
             Specify the arguments in form \"value\" to compare
             the arguments with surrounding double quotes. */
    printf("%s EQU %s\n",args[1],args[2]);

    lArgument1 = strtol(args[1],&psEnd,0);
    if(*psEnd != '\0')
    {
        if(strcmp(args[1],args[2]) == 0)
        {
            puts("The strings are equal.");
            return 0;
        }
        puts("The strings are different.");
        return 1;
    }

    lArgument2 = strtol(args[2],&psEnd,0);
    if(*psEnd != '\0')
    {
        if(strcmp(args[1],args[2]) == 0)
        {
            puts("The strings are equal.");
            return 0;
        }
        puts("The strings are different.");
        return 1;
    }

    if(lArgument1 == lArgument2)
    {
        printf("The integers %ld and %ld are equal.\n",lArgument1,lArgument2);
        return 0;
    }
    printf("The integers %ld and %ld are different.\n",lArgument1,lArgument2);
    return 1;
}

It can be already seen here on comparing this C code for demonstrating EQU behavior with the C code above for demonstrating == behavior that an integer comparison caused by using EQU results in more CPU instructions being executed than on doing a string compare with using == operator. On running the application in single step mode also into the standard library functions strcmp and strtol it is even clearer that the processor has to do many more instructions to run an integer comparison in batch file than a string comparison.

This second application written in C demonstrates perfect what happens often unexpected for batch file writers on using numbers with 1 or more leading zeros in batch file on comparing values with EQU or using them in an arithmetic expression, i.e. in string after set /A.

For example compile above code to equ.exe and run following:

@echo off
equ.exe \"08\" \"08\"
equ.exe 08 8
equ.exe 14 14
equ.exe 014 014
equ.exe 0x14 0x14
equ.exe 0x14 20
equ.exe 0x14 \"20\"

The result I get with equ.exe compiled with gpp 4.7.3 (DJGPP package) is:

"08" EQU "08"
The strings are equal.
08 EQU 8
The strings are different.
14 EQU 14
The integers 14 and 14 are equal.
014 EQU 014
The integers 12 and 12 are equal.
0x14 EQU 0x14
The integers 20 and 20 are equal.
0x14 EQU 20
The integers 20 and 20 are equal.
0x14 EQU "20"
The strings are different.

The first comparison "08" EQU "08" is executed as string comparison because of " in both arguments.

The second comparison 08 EQU 8 is finally also executed as string and not as integer comparison because first argument starts with a leading 0 and is therefore interpreted by function strtol with third parameter base being 0 as octal number which is invalid because of containing the digit 8. Valid octal numbers have only digits in range 0-7. So string to long integer conversion fails and for that reason a string comparison is executed for 08 compared with 8.

The third comparison 14 EQU 14 is executed as integer comparison with both numbers being interpreted decimal.

The fourth comparison 014 EQU 014 is executed also as integer comparison, but with both numbers being interpreted octal.

The fifth comparison 0x14 EQU 0x14 is executed again as integer comparison, but with both numbers being interpreted hexadecimal explaining twice 20 as output number.

So it is advisable to run always a string comparison of two values in batch files wherever possible with using operator == and without or with using explicitly surrounding double quotes.

It is absolutely useless to measure time differences on == versus EQU using a batch file because the time needed by Windows command interpreter to parse the command lines in batch file before executing the IF condition is many times the amount of time required for the compares itself as demonstrated by the compiled C/C++ code used internally.

Of course this also means using == or EQU does not make a real difference for the user regarding total time needed to accomplish the task done with the batch file. But for other reasons than execution time using == or EQU makes often a difference.

这篇关于批量退出for循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆