1. 程式人生 > >批處理指令碼學習筆記——程式設計師版

批處理指令碼學習筆記——程式設計師版


批處理指令碼學習筆記

說明:本文件在LGPL開源協議下發布。本文件將批處理當作一門程式語言來看待,按程式語言的元素來組織內容。這份文件是目的不是教你各個命令的語法,而主要集中在批處理是怎樣實現普通程式語言的一些功能,其中的語法說明使用的是BNF規則。文件有一定難度,不適合於連批處理是什麼都不知道的情況。如果你有點程式語言基礎那就更好了。完完整整的把批處理看下來,得到一個結論就是,批處理功能非常有限,很難當作一門完整的程式語言來看待!複製到網頁上之後格式有點亂,所以提供PDF下載

1  cmd直譯器基本工作原理

所有命令不區分大小寫,除for的迴圈變數。

cmd直譯器按邏輯行讀取和執行。行在這是的含義:1.以回車為結束標誌的一自然行。2.通過()和&&,||,&組合在一起的多個自然行。

讀取一行之後,會執行以下步驟

1)  變數替換:將引數變數(%0, %1,...,%9)和以%號引起來變數(eg: %path%)替換為實際值。

2)  去除被轉義的特殊字元的語義。轉義字元:""(對之間的所有元字元轉義), ^(對單個字元轉義)

3)  執行語法檢查,生成指令序列。

4)  執行重定向

1.1         延遲繫結技術

語法:

setlocal EnableDelayedExpansion | DisableDelayedExpansion

set var=!var!;...

...

endlocal

功能:

1)  使用變數在邏輯行執行過程中的實際值(動態變化),而不是讀取邏輯行時的值(不變,只是簡單替換)。主要用於for語句。如果不啟動的話,for語句中使用set命令時,多數情況下無法得到想要的結果。

2)  需要延遲繫結的變數,通過!var!的形式來獲取值。支援同樣的字串操作。

1.2         命令擴充套件

語法:

setlocal EnableExtensions | DiableExtensions

...

endlocal

功能:開啟一些命令的擴充套件功能。

2  批處理基本語法

註釋:

rem anything

:: anything. 直譯器無法識別::所以會拋棄這一行,達到註釋的效果。由此可以得到其它的註釋方式。

2.1         變數

2.1.1   簡單變數

定義:

set variable-name=value{value}

value := %var% | %1 | string | numbers

說明:

1) 變數名與變數值之間不能有空格

2) 變數值不能有特殊字元,如果有則需要轉義

3) 變數可以重複定義。重定義時會覆蓋之前定義的值。

4) 從使用者輸入或檔案取得變數值

set /p var=提示語句

set /p var= < file.txt

刪除:

set variable-name=

顯示:會顯示所有以variable-name為字首的變數的值

set variable-name

引用:

%變數名%

使用示例:

@echo off

set var=value

echo %var%

pause

輸出:value

2.2         輸入輸出

2.2.1   輸出

控制輸出

描述

示例

echo

輸出一行資訊

echo %var%    輸出變數值

echo %1        輸出命令列引數值

echo some-message. 輸出任意資訊

echo >>file.txt str 等同於:echo str >>file.txt

type

列印文字資訊到標準輸出

type a.txt    顯示a.txt中的內容

more

逐屏顯示

type a.txt | more +3

CLS 

清屏

路過前3行,逐屏顯示a.txt內容

COLOR 

設定cmd文字與背景顏色

color 07  背景(0:黑色)文字(7:白色)

TITLE 

設定cmd標題,常當作進度條

@title welcome to GOD 進度條程式

@

隱藏命令

@echo 不顯示命令

time /t

顯示當前時間

2.2.2   標準輸出輸出控制代碼

控制代碼

等價的數字

描述

STDIN

0

標準輸入,即鍵盤輸入

STDOUT

1

標準輸出

STDERR

2

標準錯誤輸出

UNDEFINED

3-9

應用程式定義

2.2.3   輸入輸出重定向與管道命令

命令

描述

將標準輸出重定向到檔案,即將輸出寫入檔案。以覆蓋方式寫入

>> 

將標準輸出重定向到檔案,即將輸出寫入檔案。以在尾部追加的方式寫入

將標準輸入重定向到檔案,即從檔案中讀取輸入資料

|

a | b,將a命令的輸出作為b命令的輸入。名叫管道

<&

管道合併命令。a<&b 等價於 b>&a。將要輸出到b的內容,輸出到a。將b管的出口合併到a管的入口。

常用形式:type a.txt > b.txt 2 >& 1,將type的錯誤資訊輸出到標準輸出。並將標準輸出重定向到檔案。

>&

常見:

1)  command > nul  等價於command 1>nul : 不顯示command命令的輸出資訊。

eg: copy a.txt b.txt >nul.這樣一來就不會顯示覆制完成的提示資訊了。

2)  command 2>nul : 不顯示command命令的標準錯誤資訊。

2.3         字串

2.3.1   常用字串操作

操作

描述

定義/建立

set str=a;b;c;d

連線

set str=%str%otherthings

替換

set str=%str:a=c%  把str中的所有a替換為c

set str=%str:*;=%  把str中從開始到’;’為止(包含’;’)的所有內容替換為空(即刪除)

剪下

%str:~start[,end]%

1)   字串下標從0開始。得到的字串,如果start==0, 為(str[start],str[end-1]);start!=0, 為(str[start], str[end])。

2)   start,end的取值區間:(-len, -1] & [0, len-1),負數表示從後往前數的位置。

str=a;b;c;d

%str:~1%    

;b;c;d

%str:~0,2% EQU %str:~1,2%

a;

%str:~1,-1%

;b;c;

清空

set str=

2.3.2   處理字串中的保留字元

windowsNT的保留字元:& | ( ) < > ^。當字串有以上字元只需要使用字元’^’對其進行轉義。

一般情況:

set var=dir ^> file.txt   & rem 轉義單個保留字元

set var=”dir > file.txt”  & rem 會轉義雙引號中的所有保留字元,但var中會含有雙引號

巢狀時:

@IF NOT "%~1"=="" FOR /F "tokens=2*" %%A IN (

'REG Query HKLM\SOFTWARE\PHP /v InstallDir'

) DO (

@FOR /F %%C IN (

'%%~sB.\PHP.EXE -r "print^(md5^(\"%~1\"^)^)^;"'

) DO @SET MD5=%%C

)

說明:each level of nesting would require an extra "level" ofescaping

2.3.3   示例

顯示一個變數中所有以分號分隔的字串。

@echo off

set myvar=a b;c;d

set strippedvar=%myvar%

:repeat

for /f "delims=;" %%a in ("%strippedvar%") do echo %%a

set prestrippedvar=%strippedvar%

set strippedvar=%strippedvar:*;=%

if not "%prestrippedvar:;=%"=="%prestrippedvar%" goto :repeat

2.4         算術運算SET /A

支援的數

Octal:

SET /A Result = 020

Decimal:

SET /A Result = 16

Hexadecimal:

SET /A Result = 0x10

Or any combination:

SET /A Result = 010 + 0x20 - 24

算術運算彙總:對所有操作符支援複合賦值(eg: +=, -=)

Add:

SET /A Result = 12 + 4

Subtract:

SET /A Result = 23 - 7

Multiply:

SET /A Result = 8 * 2

Integer divide:

SET /A Result = 33 / 2

Modulo divide: (12)

SET /A "Result = 66 %% 25"

Shift right: (2)

SET /A "Result = 128 >> 3"

Shift left:

SET /A "Result = 1 << 4"

Bitwise AND:

SET /A "Result = 48 & 23"

Bitwise OR:

SET /A "Result = 16 | 16"

Bitwise XOR:

SET /A "Result = 31 ^ 15"

Group:

SET /A "Result = ( 24 << 1 ) & 23"

說明:

1.取模運算,在批處理中使用%%,在命令列中使用%。

2.當表示式包含特殊字元(%&<>|ˆ(or ))時,需要使用雙引號引起來。

3.不支援實數(小數)運算

2.5         DOSKEY與命令別名

語法:

doskey macroname=[comand{$Tcommand}] 設定命令別名,等號右邊不能以空格開頭

doskey /MACROFILE=filename  從檔案中匯入命令別名設定

doskey /MACROS:ALL > filename  將所有命令別名設定匯出到檔案

Doskey巨集定義的一些特殊程式碼:

特殊程式碼

含義

示例

$T

命令分隔符。允許一個巨集中存在多個命令。

doskey ls=dir$Techo end

$1-$9

接收對應的批處理引數。與批處理程式中的 %1-%9 等同。

$*

接收別名後面的所有引數

doskey ls=dir $*

示例:

rem 在cmd啟動時設定命令別名

cmd /k doskey /macrofile=macros.linux

doskey  myname=for /f "delims=\ tokens=2" %i in ('whoami') do @echo %i

doskey destroy=del /s /q /f $*

2.6         環境變數

說明:下面兩種方式,在當前cmd例項中對環境變數作的改變,在該例項中(及其建立的子例項中,startcmd.exe)是無法獲得的。只有在下一次cmd啟動時生效;重啟explorer,批處理中才能生效。

2.6.1   使用wmic操作環境變數

wmic是一個windows系統管理工具,功能非常強大。系統支援>=xp, >=server2003。

操作

實現: wmic environment+上下面的

create name="VarName", username="<system>", VariableValue="VarValue"

where "name='Name' and username='<system>'" delete

where "name='Name' and username='<system>'" get Name, VariableValue

where "name='Name' and username='<system>'" set VariableValue="Value"

說明:

1)  可永久性的設定系統環境變數,不會因為退出cmd而失效。設定後在下一次cmd啟動時生效;重啟explorer,批處理中才能生效。

2)  set功能,如果VariableValue跟的是空值,則會刪除該變數。

3)  使用username="<system>",是用於設定系統環境變數的。如果去掉則是設定當前使用者的環境變數。

4)  不能重複create;不能set/delete/get未建立變數。

2.6.2   使用setx操作環境變數

setx說明:系統支援>=xpserver package 2。設定環境變數,永久有效。不需要重啟系統。

使用方法類似一般的set命令。

setx [/M] var-name=[var-value]

說明

1)  /M用於設定系統環境變數。

2)  只能清空,沒法刪除已經存在環境變數。

3  程式流

3.1         條件執行

3.1.1   組合命令&,&&, ||, ()

命令符號

功能描述

&

a & b, 先執行a,然後執行b。

&&

a && b, 先執行a,如果a執行成功(返回值為0)才會執行b.

||

a || b, 先執行a,如果a執行失敗(返回值非0)才會執行b.

()

用於將多行組合成邏輯上的一行命令。eg:

a

) && (

b

)

a,b命令雖然自在不同的行,但直譯器會將其當作一行處理。變數替換時會同時替換a,b中存在的變數。

3.1.2   IF基本命令

基本語法:IF [NOT] condition command1[ELSEcommand2] : 如果(不)滿足條件則執行command1,否則執行command2。中括號括起來的表示可選項。

IF命令

功能

1.IF [NOT] ERRORLEVEL number command ELSE command。

2.IF [NOT] %errorlevel% op number command ELSE command

檢查上一個命令的返回值

1.>= n. 2. op n

IF [/I] [NOT] string1 op string2 command ELSE command

比較字串/數字。/I不區別大小寫

IF [NOT] EXIST filename command ELSE command

判斷檔案(夾)是否存在

IF [NOT] DEFINED variable command ELSE command

判斷變數是否定義/不為空

說明:

1. ELSE邏輯上必須與IF在同一行上.

2. op可以上:EQU(==), NEQ(!=), LSS(<), LEQ(<=), GTR(>), GEQ(>=)

3.1.3   IF條件的布林邏輯實現

布林邏輯關係:a & b == !(!a | !b), a ^ b = (!a & b) | (a & !b)

IF...ELSE實現

臨時變數實現

布林算術實現

AND: %1 > 1 AND %2 <10 do command1

IF %1 GTR 1 (

     IF %2 LSS 10 (

        command1

)

)

也可寫為一行:

IF %1 GTR 1 IF %2 LSS 10 command1

SET flag=1

IF NOT %1 GTR 1 SET flag =0

IF NOT %2 LSS 10 SET flag =0

IF %flag% EQU 1 command1

IF %1 GTR 1 SET cond1=1 ELSE set cond1=0

IF %2 LSS 10 SET cond2=1 ELSE set cond2=0

SET /A r = "cond1 & cond2"

IF r EQU 1 command1

OR:%1 > 1 OR %1 < 10 do command1

IF %1 GTR 1 (

   command1

) ELSE (

  IF %1 LSS 10 (

command1

)

)

SET flag=0

IF %1 GTR 1 SET flag =1

IF %1 LSS 10 SET flag =1

IF %flag% EQU 1 command1

IF %1 GTR 1 SET cond1=1 ELSE set cond1=0

IF %1 LSS 10 SET cond2=1 ELSE set cond2=0

SET /A r = "cond1 | cond2"

IF r EQU 1 command1

XOR: %1 > 1 XOR %2 > 1 do command1

只用IF...ELSE...邏輯很難實現.

SET flag = 0

IF NOT %1 GTR 1 IF %2 GTR 1 SET flag = 1

IF %1 GTR 1 IF NOT EQU 1 SET  flag = 1

IF %flag% EQU 1 echo TRUE

IF %1 GTR 1 SET cond1=1 ELSE set cond1=0

IF %2 GTR 1 SET cond2=1 ELSE set cond2=0

SET /A r = "cond1 ^ cond2"

IF r EQU 1 command1

3.1.4   迴圈實現goto,label,if

label:以冒號開始.

:start

:next

:eof

goto語法:

GOTO :label

說明:程式流會從當前位置跳轉到label所在位置。

示例:解除安裝程式的選單

rem 刪除部分檔案的選單

:SELECT

    echo 選擇需要刪除的檔案

    echo [1].PGSQL,PostgreSQL資料庫程式

    echo [2].PGSQLData,資料庫資料

    echo [3].JRE,java執行時環境

    echo [4].TOMCAT, 伺服器程式

    echo [5].WEBAPP,網站主程式

    echo [6].退出

    :SELECT_AGAIN

        set /P option="輸入要刪除的專案[1|2|3|4|5|6]:"

        if "%option%" == "1" goto PGSQL

        if "%option%" == "2" goto PGSQLDATA

        if "%option%" == "3" goto JRE

        if "%option%" == "4" goto TOMCAT

        if "%option%" == "5" goto WEBAPP

        if "%option%" == "6" goto end

    goto SELECT_AGAIN

3.2         for迴圈

在命令列下for變數使用%,在批處理中for變數使用%%。

3.2.1   for變數及命令列引數擴充套件功能

語法:

%~[options]var-name

     options := option{option}

         option :=  | f | d | p | n | x | s | a | t | z | $PATH:

     var-name := [a-zA-Z0-9]

選項說明:for變數名為單個字母區分大小寫。命令列引數%0~%9也可以使用這項功能。各選項可以組合使用。

功能組合

說明

示例

%~1

刪除任何引號(")

%~1 : "C:" -> C:

%~f0

完整路徑名:驅動器號+路徑+檔名+副檔名

相當於下面四個的組合。

%~f0: D:\BatchTestDir\forexpvar.bat

%~d0

驅動器號

%~d0: D:

%~p0

路徑

%~p0: \BatchTestDir\

%~n0

檔名

%~n0: forexpvar

%~x0

一個副檔名

%~x0: .bat

%~s0

路徑,只含有短名

%~s0: D:\BATCHT~1\FOREXP~1.BAT

%~a0

檔案屬性

%~a0: --a------

%~t0

檔案的日期/時間

%~t0: 2014/04/14 11:19

%~z0

檔案的大小

%~z0: 306

%~$PATH:1

以I變數為關鍵字查詢path變數,找到則返回第一個匹配,否則返回空。

%~$PATH:1 C:\

%~dp0

驅動器號+路徑

%~dp0: D:\BatchTestDir\

示例程式碼:測試所有的變數增強功能

rem 本測試檔案完整路徑名:D:\BatchTestDir\forexpvar.bat

@echo off

call :show "C:"

goto :end

:show

echo %%~1 : %1 -^> %~1

echo %%~f0: %~f0

echo %%~d0: %~d0

echo %%~p0: %~p0

echo %%~n0: %~n0

echo %%~x0: %~x0

echo %%~s0: %~s0

echo %%~a0: %~a0

echo %%~t0: %~t0

echo %%~z0: %~z0

echo %%~$PATH:1 %~$PATH:1

echo 組合

echo %%~dp0 %~dp0

echo %%~ftza0 %~ftza0

:end

pause

3.2.2   FOR /F 分析文字

語法:

FOR /F ["options"] %variable IN (

   filenames     /*用for解析檔案內容。檔名不能含有萬用字元。eg: in (dir.txt, dir2.txt)*/

   |"string"     /*用for解析字串。字串中不能含有雙引號,否則cmd會解析出錯。*/

   |’command’    /*用for解析命令產生的資料*/

) DO command [cmd-params]

說明:

1)  按行解析文字資料。每行解析得到的資料會依次分配給for變數。

2)  options:

eol=c           - end of line. char.指一個行註釋字元的結尾(就一個)

skip=n          - skip lines count. 指在檔案開始時忽略的行數。

delims=xxx      - 指分隔符集。預設為:空格和跳格鍵。

tokens=x,y,m-n,*  - 指每行的哪些符號被傳遞到每個迭代的for變數。m-n表示範圍,*:表示額外的變數將在最後一個符號解析之後分配並接受行的其餘文字。

usebackq        - 允許在 filenames中使用雙引號擴起檔名稱。

示例:

1.分行列印path變數中的每個路徑

@echo off

set str=%path%

:next

    for /f "delims=;" %%i in ("%str%") do @echo %%i

    set prestr=%str%

    set str=%str:*;=%   & rem 刪除已經顯示的路徑

    if not "%prestr%"=="%prestr:;=%" goto :next

:end

pause

3.2.3   FOR /L,標準for迴圈

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

說明:類似於C語言中的for迴圈:for(i= start, i <= end; i+=step)...。start,end,step都可以使用變數。

set /p size=輸入迴圈次數:

for /l %i in (1, 1, %size%) do @echo %i

無限迴圈:

FOR /L %A IN (0,0,0) DO command [command-parameters]

3.2.4   FOR /R 遞迴目錄匹配檔案

語法:

FOR /R [[drive:]path]|. %variable IN (set) DO command [cmd-params]

set := keyword{,keyword}

keyword := 普通字串 | 萬用字元(*, ?)

說明:

1)  遞迴指定的根目錄(可以用’.’表示當前目錄),按set匹配檔名。將匹配到的檔案的完整路徑傳遞給for變數。

2)  set示例: *.avi(所有avi格式的檔案), *learn*.txt(檔名包含learn的文字檔案)。

示例:生成播放列表

@echo off

rem create play list.

echo 開始以當前目錄為根目錄,遞迴地生成播放列表。

setlocal EnableDelayedExpansion & rem for中使用set進行算術運算,需要啟用變數延遲繫結

set file=playlist.kpl

set n=0

echo [playlist]>%file%

for /R . %%i in (*.mp4, *.avi, *.rmvb, *.rm, *.mp3,*.ape) do (

    set /A n+=1

    echo File!n!=%%i >> %file%     & rem 檔案完整路徑

    echo Title!n!=%%~ni >> %file% & rem 檔名

    echo Length!n!=0 >> %file%

    echo Played!n!=0 >> %file% 

)

echo NumberOfEntries=%n% >> %file%

echo Version=2 >> %file%

echo CurrentIndex=47 >> %file%

endlocal

3.2.5   其它FOR功能

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

遍歷檔案。set為一個或一組檔案。

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

匹配當前目錄中的目錄,不遞迴。set中可以使用萬用字元。

3.2.6   for中使用set與延遲繫結

for中set命令失效的問題:

SET VAR=str

FOR /F "tokens=1-3 delims=;" %%i IN ("a;b;c") DO (

    SET VAR=%VAR%;%%i

    SET VAR=%VAR%;%%j

    SET VAR=%VAR%;%%k

)

echo %VAR%

結果:

;c

期待值:str;a;b;c

原因:VAR變數在for語句之前已經定義,所以cmd在解釋for語句時,會使用VAR變數的值替換for語句中的%VAR%,然後再執行for命令。

實際執行的set語句是:

set VAR=str;%%i

set VAR=str;%%j

set VAR=str;%%k

解決方案

方案一:臨時

只要在for語句之前變數VAR沒有定義,就會使用VAR的動態值。

方案二:變數延遲繫結技術

執行for語句前不會進行變數替換,直接使用變數在for語句執行過程中的值。

setlocal EnableDelayedExpansion + !var!

setlocal EnableDelayedExpansion

SET VAR=str

FOR /F "tokens=1-3 delims=;" %%i IN ("a;b;c") DO (

    SET VAR=!VAR!;%%i

    SET VAR=!VAR!;%%j

    SET VAR=!VAR!;%%k

)

echo %VAR%

endlocal

結果:

str;a;b;c

3.3         函式

3.3.1   作用域

如何在批處理中建立一個作用域,使用其中定義的變數在外部不可見。方法:

setlocal

...定義變數

endlocal

怎樣從外部取得setlocal...endlocal之間的值呢?

setlocal

...

endlocal & ( set retVal=%innerVal%)

外部就可以通過retVal訪問innerVal的值。

3.3.2   定義

rem func.bat

:func

SETLOCAL EnableDelayedExpansion

set result1=%~1   & rem 獲取函式引數

set result2=%~2

ENDLOCAL & (

  rem 設定返回值

  SET RESULT1=%RESULT1%

  SET RESULT2=%RESULT2%

  exit /B 0    &  rem 退出當前批處理指令碼,返回函式執行狀態

)

退出函式:

exit /B 0

exit /B用於退出當前批處理指令碼;從call呼叫中退出。

3.3.3   呼叫call

call可以跟檔案或標號(Label)。call 返回後會繼續執行call語句之後的程式碼。

單獨的檔案 :call func.bat param1 param2

標號Label : call :func param1, param2

3.3.4   函式返回程式碼%errorlevel%

儲存最近一個命令/函式的退出程式碼(exit...)。可以通過該變數獲得上一個命令的執行狀態。使用if語句判斷。

4  擴充套件批處理功能

4.1         模擬陣列

未直接提供,但是可以通過簡單的set進行模擬。

4.1.1   設計

模擬陣列:通過這種方式可以模擬出類似PHP中的關聯陣列。

定義:set ArrayName[%index%]=value, set ArrayName.length=%length%

取值:取單個值:%ArrayName[1]%, 遍歷:!ArrayName[%index%]!

修改:set ArrayName[%index%]=newVal

新增:set ArrayName[%index%]=value, set /A ArrayName.length+=1

刪除:set ArrayName[%index%]=   set /A ArrayName.length-=1

其中index ::= 數字 | 字串

4.1.2   實現

建立陣列:

rem CreateArray.bat

rem CALL CreateArray name 10 0

:CreateArray

    set idx=0

    set name=%~1

    set len=%~2

    set initVal=%~3

    for /L %%i IN (1, 1, %len%) do (

        set %name%[%%i]=%initVal%

    )

    set %name%.length=%len%

exit /B 0    

遍歷陣列:

@echo off

call :CreateArray names 10 1

setlocal EnableDelayedExpansion

    set idx=1

    :loopstart

if %idx% GTR %names.length% (

  goto :loopend

)

    echo !names[%idx%]!

    set /a idx+=1 & goto :loopstart

:loopend

endlocal

說明:使用for /L遍歷會失敗,原因不知。程式碼如下:

for /L %%i IN (1, 1, %names.length%) do (

    echo !names[%%i]!

)

4.2         模擬物件

4.2.1   物件模型設計

首先定義類,然後根據類建立物件。建構函式->生成並返回物件名;呼叫靜態資料初始化函式;定義非靜態資料成員。

類結構

實現方式

呼叫

建構函式

ClassConstructor [construct-params]

功能:生成唯一的物件名並返回,生成物件非靜態資料成員。

call :Constructor-Label

非靜態資料成員

ObjectName.FieldName

!%ObjectName%.FieldName!

靜態資料成員

ClassName.FieldName

%ClassName.FieldName%

非靜態成員函式

ClassName.MethodName

call :CName.MName %objname%

靜態成員函式

ClassName.MethodName

call : ClassName.MethodName

4.2.2   設計問題

n  如何生成唯一的物件名

批處理是單執行緒的,所以不會出現多個執行緒競爭的情況。cmd中可以用數字定義變數,所以物件名可以採用timestamp+random的方式,eg:201404171310269930082。並使用if defined檢查是否定義。

n  如何初始化靜態資料成員?

單獨定義一個靜態資料初始化函式。每個建構函式檢查靜態資料成員是否初始化,如果沒有則呼叫它。

n  類函式成員怎樣放置

有三種方式:

1)  在使用的位置,該類定義複製過去。使用較少時。

2)  將類成員函式放到不同的.bat檔案中。可行,檔案會過多。

3)  將一個類的定義都放到一個.bat檔案中,通過批處理引數來區別呼叫的是哪個成員函式。可行,效率低點。

4.2.3   實現

下面的程式碼,是將類定義放在使用的地方。

4.2.3.1     物件名生成器timestamp+random

rem Utility: name allocator of class.

rem return: number string of timestamp+random. eg: 201404171310269930082

:NameAlloc

setlocal

   :loopstart

   rem 2014/04/17 週四.注意delims後面有一個空格,用於斷開“週四”

   for /F "tokens=1,2,3 delims=/" %%i IN ("%date%") do set timestamp=%%i%%j%%k

   rem 14:26:24.55

for /F "tokens=1,2,3,4 delims=:." %%i IN ("%time%") do (

 set timestamp=%timestamp%%%i%%j%%k%%l%random%

   )

   if defined %timestamp% goto loopstart & 變數已定義,重新生成。

endlocal & (

    set NameAlloc.result=%timestamp%

    exit /B 0

)

4.2.3.2     類定義

rem class: Man

rem ==============constructors of Man==============

rem Man's constructor.

rem RETURN: by Man.ObjBuilder.result

rem         the name of Man's new instance.

:Man.ObjBuilder

    if not defined Man.load call :Man.Static & rem initialize static member.

    rem update static member.

    set /A Man.count+=1

    rem deifne nonstatic member of class.

    call :NameAlloc

    set %NameAlloc.result%.age=10

    set %NameAlloc.result%.name=zt

    set %NameAlloc.result%.work=writer

    rem return the name of new object.

    set Man.obj=%NameAlloc.result%

    exit /B 0

rem function: define static member of Man.

:Man.Static

rem 類載入標誌

    set Man.load=1

    set Man.count=0

    exit /B 0

rem ===============Member function of Man==========

rem non-static, %1 objname

:Man.isWriter

setlocal EnableDelayedExpansion

    set obj=%~1

    if !%obj%.work!==writer endlocal & exit /B %TRUE%

endlocal & exit /B %FALSE%

rem static function

:Man.showCount

    echo showCount:%Man.count%

    exit /B %TRUE%

4.2.3.3     類的使用

@echo off

set TRUE=0

set FALSE=1

setlocal EnableDelayedExpansion

    call :Man.ObjBuilder & rem call constructor

    call :Man.ObjBuilder

    echo %Man.obj% & rem object name.

    echo Count:%Man.count% & rem refer static member

    call :Man.showCount & rem call static member function.

    rem call non-static member function.

    (call :Man.isWriter %Man.obj%) && (echo is writer) || (echo not writer)

    rem show Man's non-static member.  

    echo Age:!%Man.obj%.age!, Name:!%Man.obj%.name!,work:!%Man.obj%.work!

    pause

endlocal

goto :eof

4.3         嵌入PHP程式碼

環境準備:

1)  下載安裝PHP

2)  配置path環境變數

嵌入PHP程式碼示例:獲取MD5

@IF NOT "%~1"=="" PHP.EXE -r "print(md5('%~1'));"

4.4         嵌入PERL程式碼

獲取MD5

@IF NOT "%~1"=="" perl -MDigest::MD5=md5_hex -le "print md5_hex '%~1'"

5  批處理任務實戰

5.1         網站部署

5.1.1   需求說明

需求:

1)  建立快捷方式以啟動tomcat。

2)  建立解除安裝程式。

3)  支援作業系統:windows, >=xp,>= server2003, x86, x64。

4)  安裝路徑可配置。

需要安裝的檔案

說明

JRE.7z

java執行時環境,手動安裝包。需要配置好JAVA_HOME,JRE_HOME,CLASSPATH等環境變數.

TOMCAT.zip

需要在conf/server.xml中配置好網站路徑<Context />。

PGSQL.zip

資料庫程式,手動安裝包。解壓會需要建立使用者、資料庫,並匯入初始化資料;配置path。

WEBAPP.7z

解壓即可

AllInOne.sql

初始化資料:表、初始化資料。

5.1.2   需要的工具與技術

需求

技術方案

解壓檔案

7z.exe, 7z.dll,32位和64位版。

建立快捷方式的工具

1.  直接使用cmd建立。過於麻煩。

2.  使用VB指令碼建立。更好。

3.  桌面位置:%USERPROFILE%\Desktop

設定環境變數

wmic,setx。優先選用wmic,因為xp中只有sp2中才有。需要使用.

識別作業系統

環境變數:%PROCESSOR_ARCHITECTURE%

可能的值: x86, x64, amd64, ia64, x86_amd64, x86_ia64

清除設定的path變數

5.1.2.1     通過.inf檔案建立快捷方式

建立如下的.inf檔案即可:

[AddLink]

setup.ini, progman.groups,, "group0=%ShortName%"

setup.ini, group0,, ""%ShortName%""

setup.ini, group0,, """%icon1name%"",""%49002%\jscript5.chm"",,0,"

具體參考:

5.1.2.2     通過VB指令碼建立快捷方式

shortcut.vbs:

set WshShell = WScript.CreateObject("WScript.Shell" )

set oShellLink = WshShell.CreateShortcut(WScript.Arguments.Named("shortcut") & ".lnk")

oShellLink.TargetPath = WScript.Arguments.Named("target")

oShellLink.WindowStyle = "1"

oShellLink.Arguments=WScript.Arguments.Named("args")

oShellLink.IconLocation=WScript.Arguments.Named("icon")

oShellLink.WorkingDirectory=WScript.Arguments.Named("wd")

oShellLink.HotKey=WScript.Arguments.Named("hotkey")

oShellLink.Save

參考:

使用示例:

    call %~dp0shortcut.vbs /target:"%ComSpec%" /args:"/c %~1\TOMCAT\bin\startup.bat" /shortcut:"%UserProfile%\Desktop\含能材料管理分析系統" /icon:"%~1\WEBAPP\favicon.ico" /wd:"%~1\TOMCAT\bin" /hotkey:"CTRL+SHIFT+F"

5.1.3   設計實現

將所有的檔案都安裝到使用者指定的一個安裝目錄中。

安裝程式目錄結構:

--

│  install.bat  根據%PROCESSOR_ARCHITECTURE%跳轉到合適的安裝檔案。

│  Readme.txt

│  unstall.bat

└─x86

    │  install.bat

    │  location.ini 安裝目錄

    │  unstall.bat

    ├─bin

    │      7z.dll

    │      7z.exe

    │      delstr.bat

    │      server.xml

    │      shortcut.vbs

    │      wmicenv.bat

    └─data

            AllInOne.sql

            dependency.txt

            JRE.7z

            PGSQL.zip

            TOMCAT.zip

            WEBAPP.7z

├─x64

│  │  install.bat

│  │  location.ini

│  │  unstall.bat

│  ├─bin

│  │      7z.dll

│  │      7z.exe

│  │      delstr.bat

│  │      server.xml

│  │      shortcut.vbs

│  │      wmicenv.bat

│  └─data

│          AllInOne.sql

│          dependency.txt

│          JRE.7z

│          PGSQL.zip

│          TOMCAT.zip

│          WEBAPP.7z

下面的程式碼以x86為例,x64版的是一樣的。

5.1.3.1     安裝指令碼主流程

@echo off

set /p loc=<%~dp0location.ini

if {%loc%}=={} (

    echo 請先在location.ini中設定安裝目錄。

    goto :install_finish

)

if not exist "%loc%" (

    md "%loc%" || goto install_finish

)

(

    call :check_files & rem 檢查需要安裝的檔案是否齊全。if exist

) && (

    call :check_already_installed "%loc%"

) && (

    call :welcome_info

) && (

    call :set_env "%loc%"  & rem call wmicenv.bat

    pause

) && (

    call :copy_files "%loc%" & rem 將檔案解壓到安裝目錄

) && (

    call :create_shortcut "%loc%"

    call :config_pgsql "%loc%"

) && (

    call :initialize_database "%loc%"

    call :start_tomcat "%loc%"

)

goto :install_finish

細節就省略,詳細的請看程式碼。

5.1.3.2     問題與解決方案

n  環境變數設定不立即生效

【問題】

使用wmic或setx。在當前cmd例項中對環境變數作的改變,在該例項中(及其建立的子例項中,start cmd.exe)是無法獲得的。只有在下一次cmd啟動時生效;重啟explorer,批處理中才能生效。

【解決方案】

所以在資料庫建立時,需要使用命令的完整路徑。

n  獲取指令碼所在目錄

【問題】

在批處理中,需要呼叫當前目錄下其它的批處理指令碼(或其它檔案)。如果直接寫指令碼名,即使與當前批處理在同一目錄中也無效。

【解決方案】

%~dp0filename

6  附錄

6.1         批處理程式碼

6.1.1    進度條顯示工具

達到的效果:

來源:http://www.robvanderwoude.com/3rdpartybatchfiles.php#ProgressMeter

@ECHO OFF

:: Input: %1 must contain the current progress (0-100)

:: Return: None

SETLOCAL ENABLEDELAYEDEXPANSION

SET ProgressPercent=%1

SET /A NumBars=%ProgressPercent%/2

SET /A NumSpaces=50-%NumBars%

:: 清空之前的內容

SET Meter=

:: Note:第二FOR的最後有一個空格

FOR /L %%A IN (%NumBars%,-1,1) DO SET Meter=!Meter!I

FOR /L %%A IN (%NumSpaces%,-1,1) DO SET Meter=!Meter!

:: Display the progress meter in the title bar and return

TITLE Progress:  [%Meter%]  %ProgressPercent%%%

ENDLOCAL & exit /B 0  & rem 退出

6.1.2   WMIC環境變數管理工具

說明:該工具可永久性的設定系統環境變數,不會因為退出cmd而失效。設定後在下一次cmd啟動時生效,不需要重啟作業系統。

rem @echo off

rem wmicenv.bat var-name [var-value]

rem Operate (system) environment permanently from cmd using wmic.exe

rem 引數:%1:name %2:value

setlocal

if "%~1"=="" goto wmicenv_usage

if "%~2"=="" goto delaction ELSE goto setaction

:setaction

    if not defined %~1 (

        wmic environment create name="%~1", username="<system>", variablevalue="%~2" || exit /B 1

    ) else (

        wmic environment where "name='%~1' and username='<system>'" set variablevalue="%~2" || exit /B 1

    )

    exit /B 0

:delaction

    if not defined %~1 (

        exit /B 0

    ) else (

        wmic environment where "name='%~1' and username='<system>'" delete || exit /B 1

    )

    exit /B 0

:wmicenv_usage

    echo %~n0的正確使用方式:

    echo 設定系統環境變數:%~n0 name value

    echo 刪除系統環境變數:%~n0 name

exit /B 1

endlocal

6.1.3    path變數刪減工具

用於從path變數中刪除一個指定條目。其實也不限於path變數,其它以’;’分隔的變數都可以。如果將for中的delims引數化,則更加靈活。

rem 功能:在一個以分號分隔的字串集合中,刪除所有等於給定字串的元素.

rem 呼叫方法:call delstr.bat string-collection-seperate-by-semicolon string-delete

rem 注意: 1.源字串以;分隔.

rem 通過變數delstr_retVal返回:失敗為-1; 成功則為處理好的字串(double-quoted)。

setlocal

    rem 刪除引數的所有雙引號, 檢查是否為空,以及是否為之前呼叫失敗返回的結果

    set strippedStr="%~1"