-
4.1 條件 if
-
4.1.1
if 是一種極其普遍卻又非常重要的語句,說得嚴重點這就是一種能夠體現程序靈魂的東西之一。在大多數的編程語言(例如 C VB JScript Java 等)中都能看到 if 的身影。if 語句的功能正如它的字面含義一樣——如果。批處理程序的語言格式相比較我們常見的 C 語言來說,并不是那么的嚴謹,至少看上去是更自由一些。比如 if 在批處理中的具體用法及格式就有很多,使用和發揮的余地也很大,但隨之帶來的問題就是我們不得不多花一些時間來記憶其各種用法和格式并分辨它們之間的差異。為了簡化該問題使之更容易理解,在此我們并不打算過早地接觸 if 的全面用法或格式,而是從最基本的用途開始。
-
4.1.2
在有前幾篇文章的學習作基礎的情況下,如果您能理解以下幾行語句,那么您已經對 if 有了深刻的認識了(其中兩個連續的等號 == 表示:是否等于)。
set var=Tom
if %var%==Tom echo It works
if %var%==Jerry echo We will never see this
如果變量 var 的值為 Tom Hanks ,即中間含有空格之類的特殊符號,那么我們在使用 if 時,就得為字符串加上雙引號,就像 if "%var%"=="Tom Hanks" echo It works (注意,給字符串加上雙引號后,在進行判斷的時候會連雙引號一起考慮進去。所以,為了使兩邊的對比均衡,所以一定要在 == 兩邊的兩個字符串上同時都加雙引號)。這里也體現了批處理程序語言格式的多樣性(如果您熟悉 C 語言格式的話,就知道一串字符總是要被雙引號引起來)。不過為了方便記憶,我們在使用 if 的時候,不妨總是在字符串上使用雙引號,這樣既好閱讀,又不容易引起歧異。
::::::::::改變顏色.bat::::::::::
@echo off
echo 您希望字體的顏色是紅色還是綠色?
:RETRY
set /p choice=請輸入您的選擇,R 或者 G :
if "%choice%"=="R" goto R
if "%choice%"=="r" goto R
if "%choice%"=="G" goto G
if "%choice%"=="g" goto G
goto RETRY
:R
color c
echo 您選擇了紅色字體
pause
exit
:G
color a
echo 您選擇了綠色字體
pause
exit
::::::::::::::::::::::::::::::::
上面的例子能很好地說明了 if 與 goto 的結合使用帶來的實用效果。其中,在使用 if 進行判斷變量 choice 的值是否為字母 R 或 G 時,分別對其大小寫都進行了判斷。其實您還可以使用 if 的參數 /i ,就像
if /i "%choice%"=="r"
,這樣在進行比較的時候就不區分大小寫了。
-
4.1.3
說到了 if 就不得不說一下 else ,else 無法單獨使用,必須與 if 配合連用。
:::::::::else的用法.bat:::::::::
@echo off
if "%TIME:~0,2%" lss "12" (
echo 現在是上午
) else (
echo 現在是下午
)
pause
::::::::::::::::::::::::::::::::
其中,變量 TIME 是動態環境變量之一,表示當前時間(在 set/?
中有介紹)。
%TIME:~0,2%
的含義還沒忘記吧,意思是取變量 TIME 的前兩個字符(忘記的朋友請參閱上一篇[賦值 調用 參數])。
lss
是 if 命令擴展用法,表示 小于 的意思。此外,還有等于、不等于、大于、大于等于、小于等于 的縮寫,詳細信息可以在 if/?
中獲得。因此,對于上述批處理的理解就是:如果當前時間(前兩位表示小時)小于12(點)的話,那么將顯示輸出“現在是上午”,否則就顯示為“現在是下午”。另外:這里的大小比較判斷只是對其ASCII符的大小比較,并不是真正的數值型變量的比較,稍后下文會有關于數值型變量比較的介紹。
對于 if 和 else 的編寫格式有較嚴格的要求,尤其是在兩個圓括號上,若以不正確的格式使用可能會導致 if 或 else 等命令無效。雖然上面的編寫格式并不是唯一的,但使用統一、固定的格式編寫代碼會大大提高代碼的可讀性。為了加深對 if else 的理解,我們可以把上面的批處理擴展一下。
:::::::::else的用法.bat:::::::::
@echo off
if "%TIME:~0,2%" lss "12" (
if "%TIME:~0,2%" lss " 6" (
echo 現在是凌晨
) else (
echo 現在是上午
)
) else (
if "%TIME:~0,2%" lss "18" (
echo 現在是下午
) else (
echo 現在是晚上
)
)
pause
::::::::::::::::::::::::::::::::
-
4.1.4 數值型變量的比較
其實很簡單,就如下面的例子中描述的一樣。
::::::::::::::::::::::::::::::::
@echo off
set /a num=5
if %num% == 5 (
echo 變量 num 等于 5
)
if not %num% == 4 (
echo 變量 num 不等于 4
)
set /a num = ( %num% + 3 ) * 2
:: 變量 num 加3并乘2后再賦給變量 num 自身
if %num% == 16 (
echo 經過運算后,現在變量 num 等于16
)
if not %num% == 16 (
echo 此時的變量 num 不會不等于 16 ,因此這一句不會顯示了
)
pause
::::::::::::::::::::::::::::::::
-
4.1.5 延遲變量擴充。
考慮到讀取一行文本時所遇到的目前擴充的限制時,延遲變量擴充是很有用的,而不是在執行的時候。下面的例子可以很好的說明直接變量擴充與延遲變量擴充的區別。
::::::::延遲變量擴充.bat::::::::
@echo off
setlocal EnableDelayedExpansion
set /a num=5
if %num% == 5 (
set /a num*=3
echo 在 if 語句之前,變量 num 等于 %num%
echo 但變量 num 在經過運算后,且由于延遲變量擴充被啟用,變量 num 等于 !num!
)
echo 但最終變量 num 還是等于 %num%
pause
::::::::::::::::::::::::::::::::
if 條件下的兩行 echo 在輸出變量值的時候用到的符號不一樣,一個是用百分號 % 包括起來的,另一個用的卻是驚嘆號 ! 。雖然在顯示%num%
之前已經使變量 num 的數值乘了3倍,但是由于沒有延遲變量的擴充,使得 %num%
的結果仍然是 5 。但用 !num! 顯示出的值已經變為 15 了。注意到批處理中的 setlocal EnableDelayedExpansion
(setlocal/?
查看相關信息),這表示開啟延遲變量擴充。此時的 !num!
才有意義。不然 !num!
將無法被識別,因為在默認情況下,延遲變量擴充是被停用的。
-
4.1.6
此外,if 還有其他的用法—— if exist 和 if defined 。if exist 可判斷文件是否存在,就像這樣:
if exist "D:\test my folder\a.txt" (
del "D:\test my folder\a.txt"
) else (
echo 您所要刪除的文件不存在
)
在對文件進行操作之前進行判斷其是否存在很有意義,這使得代碼更加健壯。
而對于 if defined 來說,與 if exist 類似,只不過 if defined 的判斷對象不是文件,而是變量,它用于判斷環境變量是否被定義。
-
4.2 循環 for
-
4.2.1
如果批處理不具備批量處理的功能,那么它就徒有虛名了。而命令 for ,在某種意義上徹底體現出了批處理的強大快捷省事批量的作用。在看過 for/?
后,可以歸納出 for 大致可以分三種常用的類型(或者叫使用方法)。從針對的循環目標來看,它們分別是針對于文件、數字、以及文字。
-
4.2.2
for %i in (*.*) do @echo %i
。
這就是 for 的一般使用格式。注意到其中的淺靛色文字 for 、in 和 do ,是 for 的固定用法。其內容可以理解為:在某一范圍內(in),對于其中的某一文件來說(for),做如下的處理(do)。而for %i in (*.*) do @echo %i
就是在當前工作目錄的所有文件中(in (.)),對于其中的某一文件(for %i),做出顯示其名稱的處理(do @echo %i)。**
變量 i 僅在當前循環語句 for 里起作用,%i 表示其值。
注意:以上是直接在命令提示符里以命令的形式表達出來的寫法;在批處理文件中應使用雙百分號 %% 代替單百分比號 %,
就像:%%i。關于它們之間的區別我研究了好半天才分清楚 orz [具體請參閱后文第4.2.5節]。
批量修改文件名是其比較有用的典例之一。看看下面的批處理
:::::::批量修改文件名.bat:::::::
@echo off
setlocal EnableDelayedExpansion
set /a num=1
for %%i in (D:\test\*.txt) do (
ren "%%i" !num!.txt
set /a num+=1
)
::::::::::::::::::::::::::::::::
這個批處理并不難理解。就像第4.1.5節所說的:使用了 setlocal EnableDelayedExpansion
后,可以讓 for 或 if 后面的執行語句中變量的值隨其變化而不斷更新(所以后面使用了!num!
而不是 %num%
)。整個批處理的處理過程就是對 D:\test\*.txt 中的所有文本文件進行批量改名,文件名從 1.txt 開始依次為 2.txt 、3.txt ……。
注意:請確保循環語句 in 路徑中的文件不是重要的文件,因為改名后將無法使用撤消,如果像我一樣不小心把重要文件誤改名的話就又要 orz 了一次。
以上批處理是固定了文件的路徑以及文件后綴名。為了增加該批處理的功能,我們可以讓用戶自己選擇要進行改名的文件所在路徑,以及選擇所進行文件修改的后綴名。當然,有些朋友還希望有給文件批量加上前綴(比如:前綴1.txt 前綴2.txt 等等)。(關于 批量改文件名.bat 在第六章中還有進一步的修改)
::::::::批量改文件名.bat::::::::
@echo off
setlocal EnableDelayedExpansion
set /p zpath=請輸入目標文件所在的路徑:
set /p prefix=請輸入文件名前綴(不能包含以下字符\/:*?"<>|):
set /p ext=請輸入文件的擴展名(例如 .txt):
set /a num=1
for %%i in (%zpath%\*%ext%) do (
ren "%%i" "%prefix%!num!.%ext%"
set /a num+=1
)
::::::::::::::::::::::::::::::::
-
4.2.3
也許大家注意到了,上面 for 的用法僅僅是針對多個文件來進行循環重復操作的。如果想對一系列有規律的數字進行循環,或是在一定的次數內對某個操作進行循環重復的執行,使用 for 也能夠實現。/l 是可以跟在 for 后面的重要參數之一。比如:for /l %i in (5,3,16) do echo %i
,可以讓數值型的變量 i 依次成為:5、8、11、14 。正如 in 里所描述的規律 (5,3,16) 一樣,從 5 開始,每次增加 3 ,直到 16為止。同樣,我們還可以試一下 for /l %i in (19,-4,3) do echo %i
,這次 i 是遞減的規律。很明顯,結果將依次顯示為:19、15、11、7、3 。
這樣的用法很自然的能讓我們想到,重復執行N遍完全一樣的事情不再是麻煩而又無聊的了。在下面的例子里,您一定會找到驚喜的。
::::::::::圓圈方陣.bat::::::::::
@echo off
setlocal EnableDelayedExpansion
set var=○
for /l %%i in (1,1,7) do (
set var=%var%!var!
echo var
)
:: 此時變量 var 已經變成一行連續的8個圓圈了
for /l %%i in (1,1,8) do (
echo 這是第 %%i 份>輸出結果%%i.txt
for /l %%j in (1,1,8) do echo %var%>>輸出結果%%i.txt
)
echo 8 X 8 的 ○ 矩陣已經畫好,并保存到8份文本文件里了
pause
::::::::::::::::::::::::::::::::
注意:%%i
,上一節中提到過,在批處理文件中需要用連續的兩個百分號 %% 來描述循環變量 i ,而不是一個。
注意:%var% 與 !var! ,它們的用法與區別,在第4.1.5節中有解釋。
注意:i 與 j ,在循環里面再套循環時,前一個循環變量 i 在沒有釋放之前,不應該讓第二個循環變量的名稱與 i 重復。
注意:> 與 >> ,同樣是向某設備里輸出,但卻有區別,請參閱第2.2節。
-
4.2.4 for 也可以對指定范圍內的文字進行循。
for 后面跟參數 /f,/f后面跟選項,所指定的范圍 in 里可以是一個文件里的文字,可以是一個字符串,也可以是一條命令的輸出結果。我們首先以一個文件里的文字作為循環對象,循環時,每一行將被循環一次。
::::::::::文字篩選.bat::::::::::
@echo off
echo 測試 文字篩選.txt 里每一行的首單詞
for /f %%i in (文字篩選.txt) do echo %%i
pause
echo.
echo skip=2 表示前兩行被跳過
for /f "skip=2" %%i in (文字篩選.txt) do echo %%i
pause
echo.
echo tokens=2,4-6 表示提取每行的第2個、以及第4到6個單詞
for /f "skip=2 tokens=2,4-6" %%i in (文字篩選.txt) do echo %%i, %%j, %%k, %%l.
pause
echo.
echo eol=N 表示當此行的首字母為 N 時,就忽略該行
for /f "eol=N skip=2 tokens=2,4-6" %%i in (文字篩選.txt) do echo %%i, %%j, %%k, %%l.
pause
echo.
echo delims=e 表示不再以空格區分每個詞,而是以字母 e 作為間隔
for /f "eol=N skip=2 tokens=2,4-6 delims=e" %%i in (文字篩選.txt) do echo %%i, %%j, %%k, %%l.
pause
echo.
echo usebackq 表示雙引號里的東西是文件名而不是字符串
for /f "usebackq eol=N skip=2 tokens=2,4-6 delims=e" %%i in ("文字篩選.txt") do echo %%i, %%j, %%k, %%l.
pause
::::::::::::::::::::::::::::::::
作為測試,可以在上述批處理文件的同一路徑下創建一個用于測試的文本文件 文字篩選.txt ,其內容為:
Hello there!
This text is an example of test for the batch file 文字篩選.bat.
Notice the first letter in this line, N.
If the eol charactor was set to be letter N.
The third line will not be considered by the batch.
-
4.2.5 ESCAPE字符 %
通常被譯為轉義字符,但也有更形象的譯名脫逸字符、逃逸字符等。也就是說 % 不僅僅將與其相關的特定字符串轉義并替換為特定字符串,而且自身也會被“脫逸”。而且類似于C語言中的轉義字符 \ ,雙%會轉義并脫逸為單百分號 % ,四%則脫為雙百分號 %% 。[3]
注意下面這個批處理中的雙百分號 %% 的用法
::::::::::::::: %% 的用法:::::::::::::::::
@echo off
set Text=Hello world!
for /l %%i IN (0,1,11) do call echo %%Text:~%%i,1%%
pause
::::::::::::::::::::::::::::::::