[英]Batch file FOR loop won't 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
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_commas%
相應的變量( %count_dots%
或%count_commas%
)中 echo
顯變量 我建議先閱讀在命令行上使用“ set var = text”后,為什么沒有以“ echo%var%”輸出的字符串? 並看這行set b = !initial!
它定義了一個環境變量,名稱為b
(不區分大小寫的小寫字母B和空格,不區分大小寫),其中一個空格和從文本文件讀取的行作為值。
Windows命令處理器不支持跳轉到FOR主體內部的標簽。
FOR主體內的標簽導致腳本執行的不確定行為。
可以使用子例程,盡管這會大大增加批處理文件的執行時間。 請參閱為什么GOTO循環比FOR循環慢得多,並且還取決於電源? 了解如何使用四種不同的解決方案解決一項任務,以及完成每種解決方案所需的時間。
這是我針對此任務的解決方案,為每行調用一個子例程以計算當前行中的逗號和點:
@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
帶選項/F
FOR laba2.txt
讀取文本文件laba2.txt
,並忽略空行。
缺省情況下,由於eol=;
, FOR也會忽略所有以分號開頭的行eol=;
是行尾選項的默認選項。 使用eol=
可以更改此行為, eol=
不指定行尾字符。
默認情況下, FOR使用正常空格和水平制表符作為字符串定界符將每一行拆分為子字符串,並僅將第一個空格/制表符分隔的字符串分配給指定的循環變量。 這里也不需要這種行拆分行為。 delims=
定義一個空的定界符列表,該列表禁止行拆分行為。
使用usebackq
選項是因為雙引號中的字符串應被解釋為應讀取行的文件名,而不是要處理的字符串。 在這種特殊情況下,不需要在文件名laba2.txt
周圍加上雙引號,這也將導致usebackq
使用usebackq
無用。
delims=
和 eol=
可以在雙引號的選項字符串中指定,但只能以此順序指定。 "usebackq eol= delims="
將不起作用,因為這將空格定義為行尾字符和空的定界符列表。
因此,具有使用的選項的FOR將將從指定文件讀取的每個非空行分配給指定的循環變量I
並在FOR循環主體中運行命令。
該行被分配到名稱為Line
環境變量旁邊。 這是在禁用延遲的環境變量擴展的情況下完成的,這對於包含一個或多個感嘆號的當前行很重要。 否則,Windows命令處理器將使用循環變量I
的當前值替換%%I
后,第二次使用啟用的延遲擴展來解析命令行set "Line=%%I"
,並解釋為!
作為其值被延遲引用的環境變量的開始/結束。 絕對不希望出現這種情況,因為這將導致在將其分配給環境變量Line
時修改該行,並且在大多數情況下會刪除該行的某些部分。
但是必須延遲擴展才能計算一行中的逗號和點。 在使用setlocal EnableDelayedExpansion
將命令行set "Line=%%I"
,並使用endlocal
作為FOR循環主體中的最后一個命令行,可以啟用延遲擴展。 但這在這里是不可能的。 由於這個原因,請閱讀此答案以及有關命令SETLOCAL和ENDLOCAL的詳細信息。
因此,使用call :Count
子例程來處理當前行。 子程序使用for循環使用選項/L
,以對抗當前行的每一個字符比較,
和.
並在到達行尾時退出,並跳轉至FOR循環下方的標簽。 與純GOTO循環相比,此FOR循環可顯着加快行處理速度,這也是可能的,但速度慢得多。
:ExitLoop
之后的命令行:ExitLoop
很特別。 endlocal
命令將恢復先前的環境,這意味着環境變量LineCountComma
和LineCountDot
不再存在,至少在子例程的FOR循環內和內部沒有設置的值。 因此,在一個命令行上指定了三個命令,由於操作符&
原因, cmd.exe
一個接一個地執行。在用三個命令執行此命令行之前, %LineCountComma%
和%LineCountDot%
被這兩個環境變量的當前值替換命令。 因此, endlocal
刪除了環境變量LineCountComma
和LineCountDot
,但是它們的值在命令行中已經是字符串,因此這些值會通過局部環境屏障。
從文本文件讀取每一行並處理子例程Count
每個非空行之后,將輸出兩個總數的值,然后還原初始環境,並使用goto :EOF
退出批處理文件執行(如果在開始此操作之前啟用了命令擴展名) Windows默認的批處理文件)。
我建議閱讀為什么Windows根本對環境變量沒有限制? 關於最大字符串長度的值8192
。 在上面的代碼中,使用set "Line=%%I"
。 從文本文件中讀取的行的最大長度可以是8183
在這種情況下字符,因為的命令處理器限制8191
字節減去 4
個字節為變量名Line
減去 1
為字節=
減去 2
個字節用於兩個雙引號和最有可能 減 1
終止空字節的字節。 因此,在實際循環中,可以使用實數8182
(字符8183的字符索引)代替循環中的8192
作為絕對最大值。
為了了解所使用的命令及其工作方式,請打開命令提示符窗口,在其中執行以下命令,並非常仔細地閱讀每個命令顯示的所有幫助頁面。
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?
也可以看看:
PS:使用Windows命令處理器cmd.exe
以外的任何其他腳本語言/解釋器設計用於執行命令和應用程序,對於計算文本文件中的逗號和點的任務肯定會更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.