Professional Documents
Culture Documents
1. 簡介說明 1.1
2. 批次檔介紹 1.2
1. 變數 1.2.1
2. 呼叫外部程式與副程式 1.2.2
3. for 迴圈 1.2.3
3. 命令列特性 1.3
4. 練習 1.4
1
簡介說明
簡介說明
Windows的自動化工作
在Windows下不安裝其他工具,就能進行自動化工作1的程式語言有3種選擇
批次檔(Batch file)
WSH(Windows Scripting Host)
PowerShell
批次檔在歷經好多年的發展之後,微軟在Windows 98的時代提出了依靠Jscript與VBscript來提
供Windows作業系統腳本語言的執行環境 WSH ,後來在提供等同於UNIX shell的強大功能與結
合.Net Framework的要求下,提出了 PowerShell。
當然除了這些原生的shell之外,還可以安裝第三方(Third party)的直譯式語言來達成自動化的
目的,例如
ActivePerl
PHP
Python
Ruby
本著登高必自卑、行遠必自邇的學習次第,先學習批次檔基本功,用來幫助與處理日常的自
動化需求, 未來再多樣化的學習各種 Script 語言,充分體會直譯式語言的魅力。
最後修訂日期:2015/10/28 by 朱孝國
1
在此指的是搭配自動化所需撰寫程式語言,這些語言都必須搭配排定的工作(scaeduled task)來
指定執行的時間,才可能達成自動化的目的。
2
批次檔介紹
批次檔介紹
1.批次檔介紹
將命令提示字元(Command Prompt)中輸入的指令集結起來,輸入在文字檔中,用以批次執
行,稱之為批次(Batch file)檔。
批次檔指令每行的長度預設為127個字元,執行後若要中斷可按下Ctrl+C。
命令提示字元預設的字碼頁為ANSI/BIG5編碼,因此檢視UTF-8編碼檔案時會出現亂碼,所以
批次檔的編寫應盡量使用 ANSI 的編碼方式,建議使用 NotePad++ 之類的有顏色與語法提示的
純文字編輯器編寫。 NotePad++目前編輯文字檔採用的編碼方式會在右下角提示,
學習批次檔等同於學習一個作業系統在命令列環境(shell)的用法,Windows環境目前有很多
Linux的影子,例如導向、管線、指令與檔案名稱補齊...等,理解之後未來接觸Linux也有很大
的幫助。
學習批次檔一般而言相當的簡單又直覺,怎樣算是學會呢,以本人的經驗,至少要將變數延
遲展開特性與 for 迴圈指令給透徹了解才算是掌握了批次檔喔。
副檔名
預設有下面這2種,在DOS與Windows 9x 時代副檔名為.bat,在Windows NT 之後則改用.cmd,
表示在視窗模式下的命令提示字元(cmd.exe)執行
bat
cmd
註解方式
標準是使用 rem,但通常會使用2個以上的冒號來當註解符號,畢竟字數比較少嘛
3
批次檔介紹
rem
::
顯示訊息
批次檔中用來顯示訊息的指令是 echo , 其後可加上字串或變數(可混搭) ,在正常的情況下,批
次檔中的每道指令執行前都會先出現螢幕上,使用 echo off 指令,就可以關閉顯示指令,通常
在不需要互動的批次檔中都一定會出現。
@ech off
set /P myname=Please input your name:
echo Hello %myname%
echo.
echo Today is %date% %time%
pause
判斷(if)
if /? 說明寫的不錯,就請直接參考囉
暫停
pause
程式暫停,提示按任意鍵繼續
timeout 秒數
程式暫停指定的秒數
回傳值
無論是在 Linux 下撰寫 shell script 或是在 Windows 下撰寫批次檔,最近一次程式執行的回傳
值判斷,在撰寫工作自動化的 Script 檔時,是非常重要的技巧。
在Windows環境中的慣例是,若指令成功時傳回0,若錯誤時,依據錯誤的狀況會傳回 1 或以
上的值,代表不同的錯誤狀況。
但並非所有指令都會根據正確或錯誤而有不同的回傳值,要用來判斷前請先測試一下。
ping 168.95.192.1
4
批次檔介紹
echo %errorlevel%
ping 123.123.123.123
echo %errorlevel%
判斷回傳值錯誤的方式有2種
1. 判斷錯誤等級變數 if %errorlevel%==1
2. 錯誤判斷子句 if errorlevel 1
命令列參數
假設在命令列鍵入了下列指令
此時批次檔內部自動將命令列上的參數視為特別的變數如下表
@ech off
echo command = %0
echo argument1 = %1
echo argument2 = %2
擴充字元參數
承上例,若在批次檔內的命令列參數加上擴充字元之後,可額外得知參數的許多資訊,請參
考下表
5
批次檔介紹
echo %~d1
echo %~p1
echo %~n1
echo %~x1
跳行與結束程式
goto 標籤 (須定義標籤,標籤須單獨一行,並以冒號開頭)
exit /b [回傳值]
設定副程式結束後回傳值為5
@echo Off
call :setError
echo %errorlevel%
goto :eof
:setError
Exit /B 5
6
批次檔介紹
7
變數
變數
變數
設定變數
批次檔使用的變數就是作業系統的環境變數,一般來說都視為字串變數,而且是是全域變
數,我們可以透過下面的指令觀察有哪些環境變數。
set
set myname=Peter
若設定的變數代表路徑時,因為路徑中可能包含空白字元,建議以雙引號含括起來較好,單
純顯示沒問題,但在命令列解析時,因為會以空白當作參數分隔,若沒有用雙引號時,會被
分開當成2個參數處理,造成錯誤。
取用變數
取用變數時,則需在變數前後加上%
echo %myname%
取消變數
取消變數時,只需依照設定變數的方式,但值是空白即可
set var=
變數的運算
set 使用 /a 參數,可使後面的敘述成為運算式
set var=6
set /a var+=3
需注意的是,若變數的值為08或09的時候,會被視為一個錯誤的8進位而其值為0,影響後續
的計算
set var=08
set /a var+=3
set var=09
set /a var+=3
輸入提示
8
變數
set 使用 /p 參數,等號(=)開始到冒號(:)結束的一段文字將視為輸入的提示
@echo off
:menu
echo 1.dir
echo 2.dir /w
echo 0.離開
set /p id=請輸入功能代碼:
if %id%==1 goto one
if %id%==2 goto two
if %id%==0 goto zero
:one
dir
goto menu
:two
dir /w
goto menu
:zero
字串的擷取
批次檔也可以像一般程式語言一樣,做到從字串的第n個位置開始擷取m個字元這件事
步驟 %date% %date:~0,4%
說明 當下日期變數 變數從第0位開始取4碼 1
值 2015/10/17 週六 2015
上述說明的程式碼如下
set today=%date:~0,4%/%date:~5,2%/%date:~8,2%
echo %today%
1字串擷取符號說明
1. 冒號(:)
2. 波浪符號(~)
3. 開始位置(從0開始)
4. 逗號(,)
5. 擷取幾個字元
字串的取代
批次檔也可以進行字串的取代,方式跟字串的擷取有點類似,下面的範例說明如何將
(140.128.71.1)替換為[140.128.71.1]。
9
變數
值 [140.128.71.1) [140.128.71.1]
上述說明的程式碼如下
@echo off
set var=(140.128.71.1)
set result=%var:(=[%
set result=%result:)=]%
echo %result%
Pause
2字串取代符號說明
1. 冒號(:)
2. 字串中想要被替換的子字串
3. 等號(=)
4. 替換後的子字串
變數延遲展開
因為批次檔的命令解譯器,會針對每一行敘述中的變數進行預處理(預先賦值)的關係,因此造
成取值的結果仍是前一個變數的狀態,必須開啟變數延遲展開(setlocal
enabledelayedexpansion),才能如同一般程式語言循序處理變數,這個特性造成很多人無法正
確駕馭批次檔的變數行為,甚為可惜。
以下會舉出幾個範例,先說明敘述中變數優先賦值的情形
請先看for 迴圈第一段的說明範例,其餘待續...
10
呼叫外部程式與副程式
呼叫外部程式與副程式
呼叫外部程式與副程式
常用的呼叫外部程式有下列幾種方式
1. call 外部程式
從批次檔中呼叫外部程式並可傳遞參數,在同一個 shell 環境下,可共同存取相同的環境
變數。
rem test.cmd
@echo off
call test1.cmd hello
pause
rem test1.cmd
@echo off
echo %1
timeout 6
2. cmd /c 外部指令
呼叫一個新的shell程式(cmd)並於指令執行完成後結束這個 shell ,返回原來的shell環
境,外部程式執行在新的shell之中,因此存取的環境變數與原程式不同,有區域變數的
遮蔽效果
@echo off
cmd /c notepad.exe
exit
@echo off
start notepad.exe
exit
副程式呼叫
批次檔的副程式呼叫也是利用 call 指令,不同的是 call 的對象不是外部程式 ,而是相同檔案
中的標籤,也可以傳遞參數。
因為批次檔的副程式僅利用標籤代表區塊的開始,因此副程式都放在程式的尾部,之後就不
要寫任何命令敘述了,代表區塊的結束。
因為批次檔循序讀取的特性,就算副程式沒有被呼叫,也會被當作標籤一般順序執行下來,
11
呼叫外部程式與副程式
@echo off
rem for迴圈使用方式請參考本手冊相關章節
for %%i in (*.dll *.exe) DO CALL :SubRoutin "%%i"
pause
goto :EOF
:SubRoutin
echo %1, %~n1, %~x1
12
for 迴圈
for 迴圈
for 迴圈
批次檔的 for 迴圈很重要,但有些特性比較隱晦,不容易駕馭,下面稍加整理需注意使用之處
以下針對第3點取值異常的情形作一說明,迴圈的敘述通常是以下列格式撰寫,左括弧與 do
同一行,右括弧放在最後面獨立成行
但其實批次檔會把迴圈內的敘述集結成一行變成
但就因為集結成一行的關係,批次檔在命令解譯器進行直譯時,會對每一行敘述中的變數進
行預先賦值的動作,因此會造成同一行敘述中,對變數改變其值卻無效的情形,請猜一下底
下這個範例會顯示的值是甚麼?
@echo off
set var=Peter
set var=John & echo %var%
timeout 6
@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set var=Peter
set var=John & echo !var!
timeout 6
批次檔中只要是利用括弧()分成多行撰寫的指令,實際上都看成一行,在括弧()裡面取用
變數時都會遇到需要開啟用延遲環境變數擴充功能的問題,一定要特別注意。
以下直接說明範例不特別解釋語法,細節請透過 for /? 學習
找出符合條件之檔案的 for 迴圈
13
for 迴圈
2. 顯示使用者目錄中的所有檔案名稱 此例必須直接在命令列輸入(注意變數名稱的差
別)、環境變數 userprofile 代表使用者目錄
找出符合條件之目錄的 for /D 迴圈
顯示使用者目錄中的所有目錄名稱 此例必須直接在命令列輸入(注意變數名稱的差
別)、環境變數 userprofile 代表使用者目錄
遞迴搜尋指定的路徑下所有符合檔案的 for /R 迴圈
可以設定開始數值、增/減數值、停止數值的 for /L 迴圈
2. 計算從1累加至100的和
set sum=0
for /L %%i IN (100, -1, 1) DO set /a sum+=%%i
echo %sum%
3. 啟用延遲環境變數擴充功能範例
SETLOCAL ENABLEDELAYEDEXPANSION
for /L %%i in (1 1 5) do (
set var=%%i
echo !var!
)
timeout 6
逐行讀取文字檔的 for /F 迴圈
for /F 是迴圈中最重要的應用,因為它可以讀取檔案、讀取字串與讀取命令,分別說明如下:
1. 讀取檔案:逐行讀取指定的檔案,然後依照分隔符號將內容賦值給指定變數
範例:逐行讀取 test.ini 文字檔內容,以等號(=)為分隔,左邊給%%i,右邊給%%j
test.ini 內容如下
account=john
passwd=a1234567890
14
for 迴圈
上述指令執行完畢之後會有下列效果, 將原來必須放在批次檔內部的變數設定,放在外
部的 ini 檔之後進行讀取,將程式碼與設定檔分離,可減少原始檔被亂改的機會
set account=john
set passwd=a1234567890
2. 讀取字串:讀取字串或變數,然後依照分隔符號將內容賦值給指定變數
範例:讀取變數%date%, 以斜線(/)為分隔,依序給%%a、%%b、%%c三變數,再依照年/
月/日的格式,儲存到mydate變數
3. 讀取命令:依照分隔符號將內容賦值給指定變數
rem 此例必須直接在命令列輸入(注意變數名稱的差別)
for /f "tokens=2" %i in ('sc query ^| find /i "SERVICE_NAME"') do @echo %i
sc query 命令會顯示目前系統所有的服務
rem 設定參數檔名與批次檔相同(.csv)
set cfg=%~n0.csv
FOR /F "tokens=1,2,3,4 delims=," %%I IN ('findstr /V [#] %cfg%') DO (
set remoteDIR=%%I
set localBackupFolder=%%J
set RetentionDay=%%K
set isCallCheckspace=%%L
)
與批次檔同名的csv檔,其內容如下
#遠端備份名稱,本地端備份路徑,備份保留天數,是否檢查硬碟剩餘空間
myweb,C:\backup,20,Y
15
命令列特性
命令列特性
2.命令列特性
多行指令合併
要將分開多行的指令寫成一行,可利用 &
切換目錄:CD
change directory的縮寫,注意絕對路徑與相對路徑的差別,鍵入cd /?可得到更多的說明
使用 /D 參數可以同時變更工作磁碟機及其工作目錄
D:
cd /d c:\windows\system32
路徑中含有空白字元時,請使用雙引號括起來
cd "\winnt\profiles\username\programs\start menu"
切換工作目錄至批次檔所在目錄
cd /d "%~dp0"
顯示目前工作目錄
echo %CD%
顯示目前磁碟機
echo %CD:~0,3%
導向(Redirect)
以命令的處理為主,輸入導向是檔案、輸出導向也是到檔案
1. 將命令的輸出導向到檔案或控制碼,對於檔案來說有新建立(>)與附加(>>)二種
2. 命令 > 檔案名稱
3. 命令 >> 檔案名稱
4. 命令 2>>檔案名稱
5. 命令 2>&1
1. 命令的輸入來自某個檔案或控制碼,只有 < 一種
6. 命令 < 檔案名稱
特殊裝置
Windows 效法了 Linux 作業系統將周邊裝置視為檔案的作法,至少有下列2種特殊裝置可視為
檔案來運作
主控台(console)
透過命令列方式直接keyin內容,以建立檔案
黑洞(nul)
一般用來將指令的正常訊息導向至黑洞,使正常訊息不要顯示在螢幕上,然後配合判斷
errorlevel 或 %errorlevel%變數,寫入log
管線(Pipe)
將命令的輸出透過管線當作另一個命令的輸入
命令1 | 命令2
dir | sort
指令的 or 、and
表面上根據 or 、and 來判斷,實際上也是透過判斷回傳值來決定,從判斷前1個命令的成功或
失敗來決定是否執行第2個命令
ping 168.95.192.1 > nul && echo %date%-%time% ping succeful >> pingTarget.
更改命令提示字元:prompt
請在命令提示字元視窗下練習輸入下面的指令,除了觀察提示字元的變化,並留意每呼叫一
次cmd,可呼叫exit返回的特性。
17
命令列特性
prompt Level1$g
cmd
prompt Level2$g
cmd
prompt Level3$g
exit
exit
可輸入prompt /? 來獲得更多的提示字元類型。
參考資源
MSDN Library 使用批次檔
Guide to Windows Batch Scripting
Top 10 DOS Batch tips
An A-Z Index of the Windows CMD command line
Getting started with batch files
Batch Utilities
18
練習
練習
3.練習
1. 請解釋此指令 sort < list.txt > alphlist.txt
2. 請解釋此指令 if errorlevel 1 echo ping target fail >> pinglog.txt
3. 請定義並顯示一個today的變數,其格式為月日年,例如10/12/2015
4. 請撰寫一個指令檔,提示使用者需輸入姓名與出生的西元年,輸入之後請顯示XXX您
好,您的年齡為:n歲
5. 請以批次檔完成九九乘法表,顯示格式如下
2x1=2
2x2=4
.....
2x9=18
3x1=3
.....
9x9=81
6. 請解釋下列指令執行的結果
@echo off
set val=1
echo %val%
(
set val=2
echo %val%
set val=3
echo %val%
)
echo %val%
pause
參考解答
1. 將 list.txt 導向輸入給 sort 命令,sort命令排序處理之後,導向輸出到 alphlist.txt
2. 若前一個指令的回傳值大於等於1,則用附加的方式導向輸出 ping taret fail 字串到
pinglog.txt 檔案
3. quiz3.cmd
@echo off
rem 答案1
set today=%date:~5,2%/%date:~8,2%/%date:~0,4%
echo %today%
rem 答案2
for /F "tokens=1,2,3 delims=/ " %%a in ('date /t') do set today=%%b-%%c-%%
echo %today%
19
練習
4. quiz4.cmd
@echo off
set /p myname=請輸入您的姓名:
set /p mybiryear=請輸入您的出生西元年:
set /a myage=2015 - mybiryear
echo.
echo %myname%您好,您的年齡為:%myage%歲
pause
5. quiz5.cmd
@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /L %%i in (2 1 9) do for /L %%j in (1 1 9) do (
set /a sum=%%i*%%j
echo %%ix%%j=!sum!
)
pause
6. 因為沒有啟用延遲環境變數擴充功能,因此在括弧內的取用(echo %val%)會先作用,因
此中間的echo 回應都是1,最後才執行變數的賦值(set val=3),所以最終echo的結果是3。
7. showip.cmd
@echo off
rem route print | for /f "tokens=4" %i in ('find "0.0.0.0"') do @echo %i &
FOR /F "tokens=4 delims= " %%i in ('route print ^| find "0.0.0.0"') do ech
:NEXT
pause
20