1. 程式人生 > >批處理指令碼——基礎語法

批處理指令碼——基礎語法

建立批處理指令碼

批處理指令碼的字尾名是.bat或者.cmd。在批處理指令碼——基礎中已經介紹過一個簡單的指令碼:

@echo "Hello world"
@PAUSE

並且這個指令碼已經能夠正常執行:

通常可以在執行完指令碼之後返回一個值,來表示這個函式執行狀態:

@echo "Hello world"
@exit /b 0

上述的exit命令就是來完成返回的操作的,注意這裡的/b表示退出當前的指令碼而不是退出cmd.exe應用,而之後接的0表示返回狀態,通常0表示正常返回,其它值表示異常返回,而異常返回使用的值由指令碼自己定義。

關於exit的說明如下:

以上就是一個最基礎的指令碼。

下面的內容開始介紹批處理的基本語法,不過在此之前說明下後續程式碼使用的程式設計環境。

之後的程式碼將使用Visual Code來說明,使用該工具可以一邊寫一邊執行,而不需要再開一個cmd.exe命令列:

關於Visual Code的介紹,可以參考Visual Studio Code使用心得

 

註釋

首先說明下批處理指令碼使用的註釋,它跟一般的語言不同,使用的註釋比較詭異。

主要有兩種,一種是"::",兩個冒號,另一種是指令REM。

:: 註釋方式1
:: @echo "Hello world"
:: @exit /b 0

@REM 註釋方式2
@REM @echo "Hello world"
@REM @exit /b 0

需要注意幾點:

1. 冒號是英文的冒號,不要搞錯寫成中文的冒號;

2. REM由於是一條命令,所以可以加@;

關於REM,再help下這條命令:

另外一點與一般註釋不同的地方是,批處理的註釋不能放在一行程式碼的末尾,不然可能產生奇怪的後果。

 

引數

批處理指令碼可以帶引數,執行時的形式如下:

檢視指令碼如下:

@echo Hello %1
@exit /b 0

使用%後接數字來表示引數,其中:

%0表示程式名稱,%1表示第一個引數,%2表示第二個引數,以此類推,預設支援到%9。

檢視下面的程式碼:

@echo Hello %1 %2 %3 %4 %5 %6 %7 %8 %9
@exit /b 0

下面是一個輸入結果:

看到如果引數不足9個,後面的就是空。

如果想要支援超過9個引數,或者想要引數的位置可變,就需要藉助於一個重要的命令:shift

shift命令的作用就是從指定位置開始(預設從/1開始)左移以引數,所以我們可以通過%n加上shift來遍歷引數,下面是一個例子:

:: 去掉不必要的列印
@echo off

:: 迴圈列印所有的引數
:LOOP
:: 如果引數是空就跳出迴圈
if "%1" == "" goto END
echo Hello %1
shift
goto LOOP

:END
@echo on
@exit /b 0

上述程式碼中的if、goto等控制命令之後會講到。下面是執行的結果:

 

變數

變數有兩種,一個是環境變數,一個是使用者自定義變數。

變數用%xx%來讀取,其中xx是變數名。

以下是一個環境變數的例子:

以下是一個使用者變數:

上述的語句也可以直接放到批處理指令碼中:

:: 環境變數
@echo %PATH%

:: 自定義變數
@set name=beni
@echo %name%

 

指令碼結構化

if-else語句

以下是關於if的介紹,直接通過help if就可以檢視,具體內容如下:

執行批處理程式中的條件處理。

IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command

  NOT               指定只有條件為 false 的情況下,Windows 才
                    應該執行該命令。

  ERRORLEVEL number 如果最後執行的程式返回一個等於或大於
                    指定數字的退出程式碼,指定條件為 true。

  string1==string2  如果指定的文字字串匹配,指定條件為 true。

  EXIST filename    如果指定的檔名存在,指定條件為 true。

  command           如果符合條件,指定要執行的命令。如果指定的
                    條件為 FALSE,命令後可跟 ELSE 命令,該命令將
                    在 ELSE 關鍵字之後執行該命令。

ELSE 子句必須出現在同一行上的 IF 之後。例如:

    IF EXIST filename. (
        del filename.
    ) ELSE (
        echo filename. missing.
    )

由於 del 命令需要用新的一行終止,因此以下子句不會有效:

IF EXIST filename. del filename. ELSE echo filename. missing

由於 ELSE 命令必須與 IF 命令的尾端在同一行上,以下子句也
不會有效:

    IF EXIST filename. del filename.
    ELSE echo filename. missing

如果都放在同一行上,以下子句有效:

    IF EXIST filename. (del filename.) ELSE echo filename. missing

如果命令擴充套件被啟用,IF 會如下改變:

    IF [/I] string1 compare-op string2 command
    IF CMDEXTVERSION number command
    IF DEFINED variable command

其中, compare-op 可以是:

    EQU - 等於
    NEQ - 不等於
    LSS - 小於
    LEQ - 小於或等於
    GTR - 大於
    GEQ - 大於或等於

而 /I 開關(如果指定)說明要進行的字串比較不分大小寫。
/I 開關可以用於 IF 的 string1==string2 的形式上。這些
比較都是通用的;原因是,如果 string1 和 string2 都是
由數字組成的,字串會被轉換成數字,進行數字比較。

CMDEXTVERSION 條件的作用跟 ERRORLEVEL 的一樣,除了它
是在跟與命令擴充套件有關聯的內部版本號比較。第一個版本
是 1。每次對命令擴充套件有相當大的增強時,版本號會增加一個。
命令擴充套件被停用時,CMDEXTVERSION 條件不是真的。

如果已定義環境變數,DEFINED 條件的作用跟 EXIST 的一樣,
除了它取得一個環境變數,返回的結果是 true。

如果沒有名為 ERRORLEVEL 的環境變數,%ERRORLEVEL%
會擴充為 ERROLEVEL 當前數值的字串表示式;否則,你會得到
其數值。執行程式後,以下語句說明 ERRORLEVEL 的用法:

    goto answer%ERRORLEVEL%
    :answer0
    echo Program had return code 0
    :answer1
    echo Program had return code 1

你也可以使用以上的數字比較:

    IF %ERRORLEVEL% LEQ 1 goto okay

如果沒有名為 CMDCMDLINE 的環境變數,%CMDCMDLINE%
將在 CMD.EXE 進行任何處理前擴充為傳遞給 CMD.EXE 的原始
命令列;否則,你會得到其數值。

如果沒有名為 CMDEXTVERSION 的環境變數,
%CMDEXTVERSION% 會擴充為 CMDEXTVERSION 當前數值的
字串符表示式;否則,你會得到其數值。

 

for語句

以下是關於for的介紹,直接通過help for就可以檢視,具體內容如下:

對一組檔案中的每一個檔案執行某個特定命令。

FOR %variable IN (set) DO command [command-parameters]

  %variable  指定一個單一字母可替換的引數。
  (set)      指定一個或一組檔案。可以使用萬用字元。
  command    指定對每個檔案執行的命令。
  command-parameters
             為特定命令指定引數或命令列開關。

在批處理程式中使用 FOR 命令時,指定變數請使用 %%variable
而不要用 %variable。變數名稱是區分大小寫的,所以 %i 不同於 %I.

如果啟用命令擴充套件,則會支援下列 FOR 命令的其他格式:

FOR /D %variable IN (set) DO command [command-parameters]

    如果集中包含萬用字元,則指定與目錄名匹配,而不與檔名匹配。

FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]

    檢查以 [drive:]path 為根的目錄樹,指向每個目錄中的 FOR 語句。
    如果在 /R 後沒有指定目錄規範,則使用當前目錄。如果集僅為一個單點(.)字元,
    則列舉該目錄樹。

FOR /L %variable IN (start,step,end) DO command [command-parameters]

    該集表示以增量形式從開始到結束的一個數字序列。因此,(1,1,5)將產生序列
    1 2 3 4 5,(5,-1,1)將產生序列(5 4 3 2 1)

FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]

    或者,如果有 usebackq 選項:

FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]

    fileset 為一個或多個檔名。繼續到 fileset 中的下一個檔案之前,
    每份檔案都被開啟、讀取並經過處理。處理包括讀取檔案,將其分成一行行的文字,
    然後將每行解析成零或更多的符號。然後用已找到的符號字串變數值呼叫 For 迴圈。
    以預設方式,/F 通過每個檔案的每一行中分開的第一個空白符號。跳過空白行。
    你可通過指定可選 "options" 引數替代預設解析操作。這個帶引號的字串包括一個
    或多個指定不同解析選項的關鍵字。這些關鍵字為:

        eol=c           - 指一個行註釋字元的結尾(就一個)
        skip=n          - 指在檔案開始時忽略的行數。
        delims=xxx      - 指分隔符集。這個替換了空格和製表符的
                          預設分隔符集。
        tokens=x,y,m-n  - 指每行的哪一個符號被傳遞到每個迭代
                          的 for 本身。這會導致額外變數名稱的分配。m-n
                          格式為一個範圍。通過 nth 符號指定 mth。如果
                          符號字串中的最後一個字元星號,
                          那麼額外的變數將在最後一個符號解析之後
                          分配並接受行的保留文字。
        usebackq        - 指定新語法已在下類情況中使用:
                          在作為命令執行一個後引號的字串並且一個單
                          引號字元為文字字串命令並允許在 file-set
                          中使用雙引號擴起檔名稱。

    某些範例可能有助:

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

    會分析 myfile.txt 中的每一行,忽略以分號打頭的那些行,將
    每行中的第二個和第三個符號傳遞給 for 函式體,用逗號和/或
    空格分隔符號。請注意,此 for 函式體的語句引用 %i 來
    獲得第二個符號,引用 %j 來獲得第三個符號,引用 %k
    來獲得第三個符號後的所有剩餘符號。對於帶有空格的檔案
    名,你需要用雙引號將檔名括起來。為了用這種方式來使
    用雙引號,還需要使用 usebackq 選項,否則,雙引號會
    被理解成是用作定義某個要分析的字串的。

    %i 在 for 語句中顯式宣告,%j 和 %k 是通過
    tokens= 選項隱式宣告的。可以通過 tokens= 一行
    指定最多 26 個符號,只要不試圖宣告一個高於字母 "z" 或
    "Z" 的變數。請記住,FOR 變數是單一字母、分大小寫和全域性的變數;
    而且,不能同時使用超過 52 個。

    還可以在相鄰字串上使用 FOR /F 分析邏輯,方法是,
    用單引號將括號之間的 file-set 括起來。這樣,該字元
    串會被當作一個檔案中的一個單一輸入行進行解析。

    最後,可以用 FOR /F 命令來分析命令的輸出。方法是,將
    括號之間的 file-set 變成一個反括字串。該字串會
    被當作命令列,傳遞到一個子 CMD.EXE,其輸出會被捕獲到
    記憶體中,並被當作檔案分析。如以下例子所示:

      FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i

    會列舉當前環境中的環境變數名稱。

另外,FOR 變數參照的替換已被增強。你現在可以使用下列
選項語法:

     %~I          - 刪除任何引號("),擴充套件 %I
     %~fI        - 將 %I 擴充套件到一個完全合格的路徑名
     %~dI        - 僅將 %I 擴充套件到一個驅動器號
     %~pI        - 僅將 %I 擴充套件到一個路徑
     %~nI        - 僅將 %I 擴充套件到一個檔名
     %~xI        - 僅將 %I 擴充套件到一個副檔名
     %~sI        - 擴充套件的路徑只含有短名
     %~aI        - 將 %I 擴充套件到檔案的檔案屬性
     %~tI        - 將 %I 擴充套件到檔案的日期/時間
     %~zI        - 將 %I 擴充套件到檔案的大小
     %~$PATH:I   - 查詢列在路徑環境變數的目錄,並將 %I 擴充套件
                   到找到的第一個完全合格的名稱。如果環境變數名
                   未被定義,或者沒有找到檔案,此組合鍵會擴充套件到
                   空字串

可以組合修飾符來得到多重結果:

     %~dpI       - 僅將 %I 擴充套件到一個驅動器號和路徑
     %~nxI       - 僅將 %I 擴充套件到一個檔名和副檔名
     %~fsI       - 僅將 %I 擴充套件到一個帶有短名的完整路徑名
     %~dp$PATH:I - 搜尋列在路徑環境變數的目錄,並將 %I 擴充套件
                   到找到的第一個驅動器號和路徑。
     %~ftzaI     - 將 %I 擴充套件到類似輸出線路的 DIR

在以上例子中,%I 和 PATH 可用其他有效數值代替。%~ 語法
用一個有效的 FOR 變數名終止。選取類似 %I 的大寫變數名
比較易讀,而且避免與不分大小寫的組合鍵混淆。