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

时间:2018-12-26 01:56:29

标签: windows file batch-file for-loop

我正在做作业。试图获取我的批处理文件以读取输入文件的各行,并弄清楚它有多少点和逗号。但是,不管我做什么,循环只会运行一次,因此即使有多行,也只能正确处理文件的第一行。我想念什么?

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%

我当然也阅读过其他类似的问题,但是我无法弄清楚这些问题如何解决我的问题,如果这是重复的话,抱歉。

哦,如果您有更好的方法来计算单个字符,我很乐意听到,但这不重要。

编辑:这是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>

这是我要读取的文件:

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

commas  and,
 dots 

2 个答案:

答案 0 :(得分:1)

  

Windows命令处理器不支持跳转到FOR主体内部的标签。 FOR主体内的标签导致脚本执行的不确定行为。

@Mofi在评论中说

所以,您不能那样做!如果理解了,您想制作一个批处理文件,该文件将查找文件有多少点和逗号。我已经完全更改了您的文件,并写了一个新文件:

@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

是的,确实很大,但是由于某种原因我无法进一步简化它:)!

此文件:

  • 将所需变量设置为0
  • 具有for循环,该循环遍历laba2.txt文本文件。
    • 仅当行(%%A)包含点(.)或逗号(,)时才会处理。
    • 通过另一个for循环查找行的大小。
    • 查找没有点(.)的线的大小。
    • 对逗号(,)相同。
    • 在两种情况下,我们都发现行的先前大小与行的当前大小存在差异。我们将结果求和到相应的变量(%count_dots%%count_commas%)中
  • echo变量
  • 删除临时文件。

答案 1 :(得分:1)

我建议先阅读Why is no string output with 'echo %var%' after using 'set var = text' on command line?,然后看一下set b = !initial!行,该行定义了一个名称为(不区分大小写的小写字母B和空格)的环境变量,并带有空格和从文本文件中读取的行作为值。

Windows命令处理器不支持跳转到 FOR 正文中的标签。
FOR 体内的标签会导致脚本执行的行为未定义。

可以使用子例程,尽管这会大大增加批处理文件的执行时间。请参见Why is a GOTO loop much slower than a FOR loop and depends additionally on power supply?,以了解如何用四种不同的解决方案解决一项任务,以及完成每种解决方案需要花费多长时间。

这是我为该任务提供的解决方案,为每行调用一个子例程以计算当前行中的逗号和点:

@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 (带有选项/F)会逐行读取文本文件laba2.txt,并忽略空行。

默认情况下, FOR 会忽略所有行,因为eol=;是行尾选项的默认选项,因此所有以分号开头的行都将被忽略。 eol=会更改此行为,该delims=不指定行尾字符。

FOR 默认情况下使用普通空格和水平制表符作为字符串定界符将每一行拆分为子字符串,并仅将第一个空格/制表符分隔的字符串分配给指定的循环变量。这里也不需要这种行拆分行为。 usebackq定义了一个空的定界符列表,该列表禁止行拆分行为。

使用

选项laba2.txt是因为双引号中的字符串应被解释为应读取行的文件名,而不是要处理的字符串。在这种特殊情况下,不需要在文件名usebackq周围加上双引号,这也将使delims=的使用变得毫无用处。

可以在双引号选项字符串中指定

eol= "usebackq eol= delims=",但只能以此顺序进行。 I不起作用,因为这将空格定义为行尾字符和空的定界符列表。

因此,带有已使用选项的 FOR 会将从指定文件读取的每个非空行分配给指定的循环变量Line,并在 FOR 正文中运行命令循环。

该行被分配到名称为set "Line=%%I"的环境变量旁边。这是在禁用延迟的环境变量扩展的情况下完成的,这对于包含一个或多个感叹号的当前行很重要。否则,Windows命令处理器将%%I替换为循环变量I的当前值后,第二次使用启用的delayed expansion解析命令行!并解释Line作为其值被延迟引用的环境变量的开始/结束。绝对不希望出现这种情况,因为这会导致在将行分配给环境变量set "Line=%%I"时修改行,而在大多数情况下会删除行的一部分。

但是必须延迟扩展才能计算一行中的逗号和点。可以在setlocal EnableDelayedExpansion命令行endlocal之后启用延迟扩展,并使用call :Count作为 FOR 循环主体中的最后一个命令行。但这在这里是不可能的。由于这个原因,请阅读this answer,其中包含有关命令 SETLOCAL ENDLOCAL 的详细信息。

因此,用/L调用子例程来处理当前行。该子例程使用带有选项, FOR 循环将当前行的每个字符与.:ExitLoop进行比较,并在到达行尾时以跳转到 FOR 循环下方的标签。与纯 GOTO 循环相比,此 FOR 循环可大大加快行处理速度,这也是可能的,但速度要慢得多。

endlocal之后的命令行还是有些特殊。命令LineCountComma将恢复先前的环境,这意味着环境变量LineCountDotcmd.exe不再存在,至少不具有上面和 FOR 子程序循环。因此,在一个命令行上指定了三个命令,&由于操作符%LineCountComma%而一个接一个地执行,从而%LineCountDot%endlocal被这两个命令的当前值替换使用三个命令执行此命令行之前的环境变量。因此,LineCountComma删除了环境变量LineCountDotCount,但是它们的值在命令行中已经是字符串,因此这些值会通过局部环境屏障。

从文本文件中读取每一行并处理子例程goto :EOF中的每个非空行之后,将输出两个总数的值,然后还原初始环境并退出8192的批处理文件执行(如果按照Windows默认设置,在启动此批处理文件之前启用了命令扩展名。)

我建议阅读Why does Windows have a limit on environment variables at all?,以获取有关最大字符串长度的值set "Line=%%I"。上面的代码中使用8183。在这种情况下,从文本文件读取的行的最大长度可以为8191个字符,这是因为命令处理器对变量名的限制为4个字节 Line个字节1 =字节,两个2 1字节, 8182字节,用于终止空字节。因此在实际循环中,可以使用8192(字符8183的字符索引)代替call /?作为绝对最大值。

要了解所使用的命令及其工作方式,请打开命令提示符窗口,在其中执行以下命令,并非常仔细地阅读每个命令显示的所有帮助页面。

  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • set /?
  • setlocal /?
  • cmd.exe

另请参阅:

PS:使用Windows命令处理器{{1}}以外的其他脚本语言/解释器设计用于执行命令和应用程序,对于计算文本文件中的逗号和点的任务肯定会更好。