简体   繁体   English

批处理文件FOR循环不会循环

[英]Batch file FOR loop won't loop

I'm doing an assignment. 我正在做作业。 Trying to get my batch file to read individual lines of an input file and figure out how many dots and commas it has. 试图获取我的批处理文件以读取输入文件的各行,并弄清楚它有多少点和逗号。 However, no matter what I do, the loop only runs once, so only the first line of the file is correctly processed, even though there are multiple lines. 但是,不管我做什么,循环只会运行一次,因此即使有多行,也只能正确处理文件的第一行。 What am I missing? 我想念什么?

REM @echo off
setlocal enabledelayedexpansion
set count=-2
for /F "delims=" %%I in (laba2.txt) DO (
set initial=%%I
set b = !initial!
:againdot
set oldb=!b!
set "b=!b:*.=!"
set /a count+=1
echo "b = !b!, oldb = !oldb!"
if not !oldb! == !b! goto :againdot
set b=!initial!
:againcomma
set oldb=!b!
set "b=!b:*,=!"
set /a count+=1
echo "b = !b!, oldb = !oldb!"
if not !oldb! == !b! goto :againcomma
)
echo %count%

I have, of course, read other similar questions, but I can't figure out how, if at all, those apply to my problem, so sorry if this is a duplicate. 我当然也阅读过其他类似的问题,但是我无法弄清楚这些问题如何(如果有的话)适用于我的问题,因此,如果这是重复的话,抱歉。

Oh, and if you have a better way to count individual characters, I'd be happy to hear it, but that's besides the point. 哦,如果您有更好的方法来计算单个字符,我很乐意听到,但这不重要。

EDIT: here's CMD feed 编辑:这是CMD提要

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>test.bat

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>REM @echo off

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>setlocal enabledelayedexpansion

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set count=-2

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>for /F "delims=" %I in (laba2.txt) DO (
set initial=%I
 set b = !initial!
 set oldb=!b!
 set "b=!b:*.=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againdot
 set b=!initial!
 set oldb=!b!
 set "b=!b:*,=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againcomma
)

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>(
set initial=this,is .a random
 set b = !initial!
 set oldb=!b!
 set "b=!b:*.=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againdot
 set b=!initial!
 set oldb=!b!
 set "b=!b:*,=!"
 set /a count+=1
 echo "b = !b!, oldb = !oldb!"
 if not !oldb! == !b! goto :againcomma
)
"b = *.=, oldb = "

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*.=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = =, oldb = *.="

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againdot

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*.=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = =, oldb = ="

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againdot

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set b=!initial!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*,=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = is .a random, oldb = this,is .a random"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againcomma

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set oldb=!b!

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set "b=!b:*,=!"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>set /a count+=1

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo "b = !b!, oldb = !oldb!"
"b = is .a random, oldb = is .a random"

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>if not !oldb! == !b! goto :againcomma

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>echo 3
3

C:\Users\fiksi\Desktop\Учёбка\ОС 3с\Лаба 2>

And here's the file I'm trying to read: 这是我要读取的文件:

this,is .a random
..,arran,gement of.

commas  and,
 dots 

Windows command processor does not support jump to a label inside body of FOR. Windows命令处理器不支持跳转到FOR主体内部的标签。 Labels inside body of FOR result in an undefined behavior of script execution. FOR主体内的标签导致脚本执行的不确定行为。

@Mofi said in comments @Mofi在评论中说

So, you can't do it that way! 所以,您不能那样做! If understood you want to make a batch file which will find how many dots and commas a file has. 如果理解了,您想制作一个批处理文件,该文件将查找文件有多少点和逗号。 I have changed your file entirely and wrote a new one: 我已经完全更改了您的文件,并写了一个新文件:

@echo off

set /a "count_dots=0"
set /a "count_commas=0"

powershell.exe -NoP -C "(Get-Content laba2.txt) -Replace '!',''|Set-Content $env:TEMP\laba2_tmp.txt"
setlocal EnableDelayedExpansion
for /f "delims= eol=" %%A IN (%TEMP%\laba2_tmp.txt) do (
    set line=%%A

    rem Find size of variable:
    (echo !line!)>%TEMP%\temp.txt
    for %%B IN (%TEMP%\temp.txt) do set size=%%~zB

    rem Check if the line has got commas:
    if not "!line:,=!" == "!line!" (
        rem Find size of variable *without* commas:
        (echo !line:,=!)>%TEMP%\temp.txt
        for %%B IN (%TEMP%\temp.txt) do set sizecommas=%%~zB

        rem Find difference of previous and new size of variable:
        if !size! NEQ !sizecommas! (set /a "diffcommas=size-sizecommas" & set /a "count_commas+=diffcommas")


    rem Check if the line has got dots:
    if not "!line:.=!" == "!line!" (
        rem Find size of variable *without* dots:
        (echo !line:.=!)>%TEMP%\temp.txt
        for %%B IN (%TEMP%\temp.txt) do set sizedots=%%~zB

        rem Find difference of previous and new size of variable:
        if !size! NEQ !sizedots! (set /a "diffdots=size-sizedots" & set /a "count_dots+=diffdots")
        )
    )
)

rem Delete temporary files:
del %TEMP%\temp.txt %TEMP%\laba2_tmp.txt

if %count_dots% == 1 (echo Found %count_dots% dot in specified document.) else (echo Found %count_dots% dots in specified document.)
if %count_commas% == 1 (echo Found %count_commas% comma in specified document.) else (echo Found %count_commas% commas in specified document.)

pause

Yes, it's true that it is quite big, but I couldn't simplify it further for a reason :)! 是的,的确很大,但是由于某种原因我无法进一步简化它:)!

This file: 这个文件:

  • Sets wanted variables to 0 . 将所需变量设置为0
  • Has a for loop which loops through laba2.txt textfile. 有一个for循环,该循环遍历laba2.txt文本文件。
    • Only if line ( %%A ) contains dots ( . ) or commas ( , ) will be processed. 仅当行( %%A )包含点( . )或逗号( , )时才进行处理。
    • Finds size of line with another for loop. 查找与另一个for循环的行的大小。
    • Finds size of line without dots ( . ). 查找带点( . )的线的大小。
    • Does the same for commas ( , ). 对逗号( , )执行相同的操作。
    • In both cases we find difference of previous size of line and current size of line. 在这两种情况下,我们都发现行的先前大小与行的当前大小存在差异。 We sum the result in the corresponding variable ( %count_dots% or %count_commas% ) 我们将结果%count_commas%相应的变量( %count_dots%%count_commas% )中
  • echo the variables echo显变量
  • Remove temporary files. 删除临时文件。

I recommend first reading Why is no string output with 'echo %var%' after using 'set var = text' on command line? 我建议先阅读在命令行上使用“ set var = text”后,为什么没有以“ echo%var%”输出的字符串? and look on the line set b = !initial! 并看这行set b = !initial! which defines an environment variable with name b (case-insensitive interpreted small letter B and space) with a space and the line read from text file as value. 它定义了一个环境变量,名称为b (不区分大小写的小写字母B和空格,不区分大小写),其中一个空格和从文本文件读取的行作为值。

Windows command processor does not support a jump to a label inside body of FOR . Windows命令处理器不支持跳转到FOR主体内部的标签。
Labels inside body of FOR result in an undefined behavior of script execution. FOR主体内的标签导致脚本执行的不确定行为。

It is possible to use a subroutine although that dramatically increases the batch file execution time. 可以使用子例程,尽管这会大大增加批处理文件的执行时间。 See Why is a GOTO loop much slower than a FOR loop and depends additionally on power supply? 请参阅为什么GOTO循环比FOR循环慢得多,并且还取决于电源? to see how one task was solved with four different solutions and how long it take to finish the task with each solution. 了解如何使用四种不同的解决方案解决一项任务,以及完成每种解决方案所需的时间。

Here is my solution for this task calling a subroutine for every line to count commas and dots in current line: 这是我针对此任务的解决方案,为每行调用一个子例程以计算当前行中的逗号和点:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "TotalCountComma=0"
set "TotalCountDot=0"

for /F "usebackq delims= eol=" %%I in ("laba2.txt") do (
    set "Line=%%I"
    call :Count
)

if %TotalCountComma% == 1 (set "CommaS=") else set "CommaS=s"
if %TotalCountDot% == 1 (set "DotS=") else set "DotS=s"
echo The file contains %TotalCountComma% comma%CommaS% and %TotalCountDot% dot%DotS%.
endlocal
goto :EOF

:Count
setlocal EnableDelayedExpansion
set "LineCountComma=0"
set "LineCountDot=0"
set "LineLength=1"
for /L %%J in (0,1,8192) do (
    if "!Line:~%%J,1!" == "" goto ExitLoop
    if "!Line:~%%J,1!" == "," set /A LineCountComma+=1
    if "!Line:~%%J,1!" == "." set /A LineCountDot+=1
)
:ExitLoop
endlocal & set /A "TotalCountComma+=%LineCountComma%" & set /A "TotalCountDot+=%LineCountDot%"
goto :EOF

FOR with option /F reads text file laba2.txt line by line and ignores empty lines. 带选项/F FOR laba2.txt读取文本文件laba2.txt ,并忽略空行。

There are ignored by default by FOR also all lines starting with a semicolon because of eol=; 缺省情况下,由于eol=;FOR也会忽略所有以分号开头的行eol=; is the default for end of line option. 是行尾选项的默认选项。 This behavior is changed with eol= which specifies no end of line character. 使用eol=可以更改此行为, eol=不指定行尾字符。

FOR splits up by default every line into substrings using normal space and horizontal tab as string delimiters and assigns just first space/tab delimited string to specified loop variable. 默认情况下, FOR使用正常空格和水平制表符作为字符串定界符将每一行拆分为子字符串,并仅将第一个空格/制表符分隔的字符串分配给指定的循环变量。 This line splitting behavior is not wanted here, too. 这里也不需要这种行拆分行为。 delims= defines an empty list of delimiters which disables the line splitting behavior. delims=定义一个空的定界符列表,该列表禁止行拆分行为。

Option usebackq is used because of the string in double quotes should be interpreted as file name of which lines should be read and not as string to process. 使用usebackq选项是因为双引号中的字符串应被解释为应读取行的文件名,而不是要处理的字符串。 In this special case the double quotes around file name laba2.txt would not be necessary which would make also usage of usebackq useless. 在这种特殊情况下,不需要在文件名laba2.txt周围加上双引号,这也将导致usebackq使用usebackq无用。

delims= and eol= can be specified in a double quoted options string, but only in this order. delims= eol=可以在双引号的选项字符串中指定,但只能以此顺序指定。 "usebackq eol= delims=" would not work because of this defines space as end of line character and an empty list of delimiters. "usebackq eol= delims="将不起作用,因为这将空格定义为行尾字符和空的定界符列表。

So FOR with the used options assigns every non-empty line read from specified file to specified loop variable I and runs the commands inside body of FOR loop. 因此,具有使用的选项的FOR将将从指定文件读取的每个非空行分配给指定的循环变量I并在FOR循环主体中运行命令。

The line is assigned next to environment variable with name Line . 该行被分配到名称为Line环境变量旁边。 This is done while delayed environment variable expansion is disabled which is important on current line containing one or more exclamation marks. 这是在禁用延迟的环境变量扩展的情况下完成的,这对于包含一个或多个感叹号的当前行很重要。 Otherwise Windows command processor would parse with enabled delayed expansion the command line set "Line=%%I" a second time after replacing %%I by current value of loop variable I and would interpret ! 否则,Windows命令处理器将使用循环变量I的当前值替换%%I后,第二次使用启用的延迟扩展来解析命令行set "Line=%%I" ,并解释为! as beginning/end of an environment variable of which value is referenced delayed. 作为其值被延迟引用的环境变量的开始/结束。 This behavior is definitely not wanted here as it would result in modifying the line on assigning it to environment variable Line with in most cases removing parts of the line. 绝对不希望出现这种情况,因为这将导致在将其分配给环境变量Line时修改该行,并且在大多数情况下会删除该行的某些部分。

But delayed expansion is necessary to count the commas and dots in a line. 但是必须延迟扩展才能计算一行中的逗号和点。 It would be possible to enable delayed expansion after the command line set "Line=%%I" with setlocal EnableDelayedExpansion and use endlocal as last command line in body of FOR loop. 在使用setlocal EnableDelayedExpansion将命令行set "Line=%%I" ,并使用endlocal作为FOR循环主体中的最后一个命令行,可以启用延迟扩展。 But this is not possible here. 但这在这里是不可能的。 For the reason read this answer with details about the commands SETLOCAL and ENDLOCAL . 由于这个原因,请阅读此答案以及有关命令SETLOCALENDLOCAL的详细信息。

Therefore a subroutine is called with call :Count to process the current line. 因此,使用call :Count子例程来处理当前行。 The subroutine uses a FOR loop with option /L to compare every character of current line against , and . 子程序使用for循环使用选项/L ,以对抗当前行的每一个字符比较,. and which is exited on reaching end of line with a jump to a label below the FOR loop. 并在到达行尾时退出,并跳转至FOR循环下方的标签。 This FOR loop speeds up line processing dramatically in comparison to a pure GOTO loop which would be also possible, but is much slower. 与纯GOTO循环相比,此FOR循环可显着加快行处理速度,这也是可能的,但速度慢得多。

The command line after :ExitLoop is again something special. :ExitLoop之后的命令行:ExitLoop很特别。 The command endlocal would restore previous environment which means the environment variables LineCountComma and LineCountDot do not exist anymore, at least not with the values set above and inside the FOR loop of the subroutine. endlocal命令将恢复先前的环境,这意味着环境变量LineCountCommaLineCountDot不再存在,至少在子例程的FOR循环内和内部没有设置的值。 So there are three commands specified on one command line which cmd.exe executes one after the other because of operator & whereby %LineCountComma% and %LineCountDot% are replaced by the current values of those two environment variables before executing this command line with the three commands. 因此,在一个命令行上指定了三个命令,由于操作符&原因, cmd.exe一个接一个地执行。在用三个命令执行此命令行之前, %LineCountComma%%LineCountDot%被这两个环境变量的当前值替换命令。 So endlocal deletes the environment variables LineCountComma and LineCountDot , but their values are already as strings on the command line and therefore the values pass the local environment barrier. 因此, endlocal删除了环境变量LineCountCommaLineCountDot ,但是它们的值在命令行中已经是字符串,因此这些值会通过局部环境屏障。

After reading every line from text file and processing every non-empty line in subroutine Count , the values of the two total counts are output before restoring the initial environment and exiting batch file execution with goto :EOF (if command extensions were enabled before starting this batch file as by Windows default). 从文本文件读取每一行并处理子例程Count每个非空行之后,将输出两个总数的值,然后还原初始环境,并使用goto :EOF退出批处理文件执行(如果在开始此操作之前启用了命令扩展名) Windows默认的批处理文件)。

I recommend reading Why does Windows have a limit on environment variables at all? 我建议阅读为什么Windows根本对环境变量没有限制? regarding to value 8192 for maximum string length. 关于最大字符串长度的值8192 In the code above is used set "Line=%%I" . 在上面的代码中,使用set "Line=%%I" The maximum length of the line read from text file can be 8183 characters in this case because of command processor limit of 8191 bytes minus 4 bytes for variable name Line minus 1 byte for = minus 2 bytes for the two double quotes and most likely minus 1 byte for terminating null byte. 从文本文件中读取的行的最大长度可以是8183在这种情况下字符,因为的命令处理器限制8191字节减去 4个字节为变量名Line 减去 1为字节= 减去 2个字节用于两个双引号和最有可能 1终止空字节的字节。 So in real 8182 (character index of character 8183) could be used instead of 8192 in the loop as absolute maximum. 因此,在实际循环中,可以使用实数8182 (字符8183的字符索引)代替循环中的8192作为绝对最大值。

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 /?
  • for /?
  • goto /?
  • if /?
  • set /?
  • setlocal /?

See also: 也可以看看:

PS: The usage of any other scripting language/interpreter than Windows command processor cmd.exe designed for execution of commands and applications would be definitely better for the task counting commas and dots in a text file. PS:使用Windows命令处理器cmd.exe以外的任何其他脚本语言/解释器设计用于执行命令和应用程序,对于计算文本文件中的逗号和点的任务肯定会更好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM