今天想利用 BAT 檔來完成 檔案的複製工作,
像是 "定期備份桌面的檔案" 之類的阿~
要是滑鼠點下去就自動完成多棒啊!(所謂懶惰是科技創新的動力XD)
要生出一個批次檔其實不難:1) 在記事本裡打一些文字,2) 另存成 .bat 檔,就這樣。
難是難在語法不曉得怎麼用啊!! XD
於是我就查了查 DOS 批次檔語法~
解釋一下:
BAT 是 Batch 的簡寫,因此 Batch Language 就是批次檔所使用的語法喔!
DOS 是 Microsoft 的命令提示字元 (cmd),也就是黑底白字加新細明體的那個啦XD
換句話說,這邊的指令只適用 Windows 喔! MacOS 的 terminal 指令是不一樣的~
以下直接以實例來講解,講解部分都很精簡,請自己試一試、揣摩看看來幫助理解喔!
範例 1:
@ECHO OFF
CD C:\Program Files (x86)\Google\Chrome
PAUSE
REM 複製到 D:\ 底下~~~
COPY *.txt D:\David\備份BA~1\電腦\Chrome
ECHO finish
第一行打 "ECHO OFF",可以讓後續的指令都不會出現在螢幕上,改成 "ECHO ON" 的話則會出現;至於 "@" 則是讓第一行本身也不顯示~
第二行 CD <路徑> 就是前往那個路徑,這是基本的 DOS 語法(不知道的請見下面連結)
第三行 PAUSE 會暫停,cmd 視窗就會停住,等你按任意鍵繼續
第四行 REM 是註解 (remark),註解是給人看的不是給機器看的,DOS 會略過此行
第五行 COPY *.txt <路徑>,是複製所有 txt 結尾的檔案,到目標資料夾那裡( * 是萬用字元,也是基本 DOS 語法喔)
第六行 ECHO finish,會在螢幕上顯示 ECHO 後面的字串,所以就會顯示 "finish"
注意第五行的:
COPY *.txt D:\David\備份BA~1\電腦\Chrome
它完整的路徑其實是 "D:\David\備份 Backups\電腦\Chrome"
但是因為 "備份 Backups" 這個檔名裡面含有空格,DOS 無法接受,所以需要轉換成 8.3 format,
轉換方法是在某個目錄底下打 "dir/x",就會以 8.3 的格式列出目錄底下的檔名了~
常用 DOS 語法:https://mrtang.tw/blog/post/9751934 (有提到萬用字元 * )
https://mrtang.tw/blog/post/9848241 (講解 CD, MD, RD, DIR)
https://mrtang.tw/blog/post/10779313 (講解 REN, TYPE, ATTRIB)
BAT 批次檔語法:http://33tsai.blogspot.tw/2008/04/bat.html
上面是第一頁,第二頁在這邊:http://33tsai.blogspot.tw/2008/04/bat_21.html,本篇主要都是參考這兩篇的喔~
檔名轉換成 DOS 8.3 格式:https://community.microfocus.com/adtd/silktest/w/wikiid-128/35565/how-can-i-get-a-file-name-in-dos-8-3-format
範例 2:
@ECHO OFF
IF "%1" == "A" (ECHO Apple)
IF "%1" == "B" (ECHO Banana)
輸入:D:\test.bat A
輸出:Apple
假設這個程式叫做 "test.bat",並且是放在 D:\ 底下,
那麼,當我們在 cmd 中輸入 "D:\test.bat A" 時,"A" 這個參數就會被存進 %1 裡面;
因此,第二行就會變成 IF "A" == "A",條件會成立,所以會執行後面的 ECHO 而產生以上的輸出。
補充一點:最多可以傳送九個參數給 BAT 喔,會對應到 %1 ~ %9。
範例 3:
@ECHO OFF
CD D:\David\
IF EXIST %1 GOTO PrintFile
GOTO FileNotExist
:PrintFile
TYPE %1
GOTO End
:FileNotExist
ECHO File Not Exist!
GOTO End
:End
輸入:D:\test.bat list.txt
輸出:(list.txt 檔案裡的內容)
假設這個程式叫做 "test.bat",並且 "D:\David\" 底下有個叫 "list.txt" 的文字檔;
當我們提供上面那一行輸入時,程式就會把 .txt 裡面的內容印出來;而若找不到有此名稱的檔案,就會印出 "File Not Exist!"
第三行,IF EXIST <完整路徑加上檔名> <動作> ;理論上要提供完整的路徑 (eg. D:\David\list.txt),但因為我們已經 CD 到同一個資料夾中了,所以提供 "list.txt" 即可。另外,其變種為 IF NOT EXIST <.....>
接著 GOTO PrintFile,效果是直接跳到第五行 ":PrintFile" 那裡執行,也就是說中間的都會略過不執行;標籤的名字可以隨便取,像是 "ABC_NAME001" 等等
第四行,如果第三行沒有跳走的話,就會 GOTO 跳到 FileNotExist
第五~七行,是 PrintFile 所要執行的內容;TYPE <檔名> 會把那個檔案印出來,eg. TYPE list.txt
第七行,執行完記得要再 GOTO 到底下的 :End,否則它會繼續往下執行第八行、第九行喔!
第八~十行,是 FileNotExist 所要執行的內容
到這邊,大家應該已掌握了基本的 CD、IF、參數以及 GOTO 了吧!
接下來我想介紹一下如何使用迴圈、讀/寫檔案以及使用函式~(難度較高請自行斟酌XD)
範例 4:
@echo off
REM ***** 1) 重複三次,共三秒 *****
for /L %%I in (1,1,3) do (
call :ShowTime
timeout /t 1 >nul
)
goto :eof
:ShowTime
REM ***** 2) 將現在時間 (HH:MM:SS) 存進 now *****
for /F "tokens=1 delims=." %%A in ("%time%") do (set now=%%A)
REM ***** 3) 切割出 HH, MM, SS *****
for /F "tokens=1-3 delims=:" %%A in ("%now%") do (
echo 現在時間是 %%A 點 %%B 分 %%C 秒
)
exit /b
輸入:D:\test.bat
輸出:現在時間是 19 點 02 分 34 秒
第一行,再熟悉不過的 @ECHO OFF;不過不太一樣的是這次我改用小寫,因為 BAT 裡面其實都 OK。
第三行,for /L %%I in (開始, 增加, 結束) do (動作),是一種產生數字的 FOR 迴圈。簡單來說,%%I 會從 1 開始,每次增加 1,一直到 3 結束。
第四行,call :<標籤>,效果是直接跳到標籤那行(也就是 :ShowTime),直到撞見 exit /b 才會跳回來
第五行,timeout /t 1 會停頓一秒鐘。後面的 ">null" 是重新導向的意思,目的是讓它閉嘴,默默的停頓就好XD
換句話說,FOR 會執行三次,每次 CALL 完會停頓一秒;接下來我們來看 :ShowTime 裡面在幹嘛:
註解 2) 底下:for /F "額外選項" %%A in ("字串") do (動作),是 FOR /F 的三種用法之一(見底下連結)
- 首先,我們的字串 "%time%" 其實是 "19:02:34.76",因為 %TIME% 能夠取得現在的時間
- 接下來,FOR 會把字串切割成 "19:02:34" 以及 "76",因為 "delims=." 相當於告訴它,使用點去做切割
- 切割完之後,"tokens=1" 相當於告訴它,我們只要第一個結果(紅字);它會存到 %%A 裡面
- 最後,括號內的動作是 "set now=%%A",意思是把結果存到 now 變數中。
註解 3) 底下:同樣是字串切割的 FOR /F。這次我們使用冒號做切割,並取得第 1~3 個結果(就是全部啦)
- 字串 "%now%" 會替換成剛剛的 "19:02:34",因為變數的前後接上 % 就會跑出裡面的值
- 切割完,第一個結果會在 %%A,第二個在 %%B,第三個在 %%C,以此類推。
最後,goto :eof 會結束目前的程式(如果在函式中就會結束函式)
Guide to Windows Batch Scripting:https://steve-jansen.github.io/guides/windows-batch-scripting/index.html
英文網站,有提到很多東西 (算式、setlocal、%~n、errorlevel、重導向、函式),看不懂就再留言吧~
命令提示字元 19:迴圈進階:https://lnpcd.blogspot.com/2012/09/19.html
介紹 FOR /F 的三種用法!
Windows CMD SS64:https://ss64.com/nt/set.html
這個是以 SET 為例,它有最詳盡的使用說明!(應該說它就是說明書,最詳盡但未必好懂就是了)你可以在搜尋框框打任何想查詢的其他指令~
範例 5:
@echo off
set now=%time:~0,8%
REM ***** 1) 從 time.txt 中讀取上次的時間 *****
if exist time.txt (
for /F "delims=" %%A in ('type time.txt') do (set last_time=%%A)
) else (
set last_time=%now%
)
call :CalculateDiff %last_time% %now%
echo 距離上次執行過了 %diff% 秒
echo %now%>time.txt
goto :eof
:CalculateDiff
REM ***** 2) 計算兩者 (HH:MM:SS) 相差幾秒,存進 diff *****
for /F "tokens=1-3 delims=:" %%A in ("%1") do (
set /A "sec1=%%A*60*60 + %%B*60 + %%C"
)
for /F "tokens=1-3 delims=:" %%A in ("%2") do (
set /A "sec2=%%A*60*60 + %%B*60 + %%C"
)
set /A "diff=sec2-sec1"
exit /b
輸入:D:\test.bat
輸出:距離上次執行過了 176 秒
第二行,%time:~0,8% 會直接從 "19:02:34.76" 當中取得 "19:02:34"(此為子字串的語法,見底下 QA)
註解 1) 底下,我們會先檢查 time.txt 檔案是否存在。
- 如果存在,for /F "額外選項" %%A in ('指令') do (動作) 會把指令(type time.txt)的結果存到 %%A 中(注意這邊是使用單引號而非雙引號喔!)
- "delims=" 會告訴它,不要使用任何字元做切割。所以整句話的效果即:將檔案的內容存進 last_time 中。
- 如果不存在,我們就把 last_time 設為現在的時間。
接下來,我們會呼叫 :CalculateDiff 函式做一些計算。注意,函式呼叫時是可以加上額外參數的,這些參數進到函式中就會變成 %1、%2 等。
註解 2) 底下,第一個 FOR /F 會將時間切割成小時、分鐘及秒鐘。
- set /A "變數=數學計算式" 會先做數學計算,算完再將結果存進變數中。
- 因此,sec1 及 sec2 會是換算成秒數的時間;兩者相減後會被存進 diff,然後程式會跳回去 call 的地方。
結束前,我們使用重導向符號 ">" 將 %now% 寫到檔案中,這樣下次就能算出正確的 "上次執行時間" 了。
本篇只教了核心的用法,還有很多東西是要靠自己查詢 (eg. 手冊) 或讀其他文章 (eg. 英文QQ) 來學習的。
以下整理了一些我個人遇到過或者網友提問過的問題~
Q1: Batch 如何用正規表示式 (RegEx) 來篩選特定的字串呢?
Ans: 使用 findstr /r (也可搭配 errorlevel)
你可以來實驗看看以下這段,之後再試著把var 改成 dbcefg 看看有何不同,最後再試著把第二行框框裡的改成 [a-z]
set var=abcefg
echo %var% | findstr /r "^[a-c]">nul
if errorlevel 1 (echo no) else (echo yes)
第一行就是簡單的設定變數,將var 設定成那串字
第二行首先,就是先把var 給印出來(記得,變數取用時需要在前後都加上%)
但是後面有個 "|",是重新導向的意思,所以原本要印到螢幕上的字,就丟給了後面的 findstr 處理。
"^[a-c]" 意思是以 a~c 字母開頭者,像是 ^[a-zA-Z0-9],就相當於尋找所有的英文及數字開頭者。(詳情請google正規表示式)
最後的 ">" 也是重新導向的意思,丟給nul 的目的是讓它不要顯示出來。你可以試試看把 ">nul" 拿掉,結果就會被印到螢幕上了。
第三行是依據 errorlevel 的值,來決定要顯示 yes or no。原理是這樣的:任何指令都會有回傳值,照理說如果正確執行了,就會回傳 0,若失敗了就不是 0。
而errorlevel 會保存上一行指令的最終回傳值,因此若 findstr 有成功找到,則回傳值為 0,反之則大於 0。
Q2: 如何正確的 echo Parentheses () 以及 Angle brackets <> ?例如 if 1 == 1 (echo (abc)) 就會跳出錯誤。
Q3: 如何檢查執行 script 的人是否有 Admin 權限?
Q4: 如何把指令的結果存到一個變數當中?
Q5: 如何取得子字串 (Substring)?
Q6: 如何在 IF ... ELSE 的條件裡面使用 AND 或是 OR?
Ans: 沒辦法,但可以用一些迂迴的方式,例如想要使用 AND 的話,可以:
if %a% == 1 if %b% == 1 (echo Both are true.)
要達成 OR 的效果,則可以這樣:
set res=
REM 上面這行很重要!因不能保證 res 之前存的是什麼
if %a% == 1 (set res=1) else if %b% == 1 (set res=1)
if defined res (echo One of them is true.)
Q7: 如何從一個 BAT 腳本呼叫其他的 BAT 腳本?
Ans: 請使用 CALL "D:\其他腳本的路徑.bat"。
Q8: 註解應該要用 REM 還是 :: 哪個比較好?

哈囉 想問我現在有個bat檔她開啟時都是在某個資料夾內部的檔案 @echo off set PATH=%CD%\MinGW\bin:%PATH% "%CD%\MinGW\msys\1.0\msys.bat" 我想把它改成開啟以後路經在我桌面上該怎麼改? c:\users\g552\desktop 謝謝!!
你好,不好意思忘記回覆你的留言了>< 你是指說,這個 bat 檔開啟時的路徑(%CD%)都是在某個資料夾內部, 但你希望路徑改成在桌面,是這個意思嗎? 如果是這樣,那你只需要加上這一行就可以了喔~ CD C:\users\g552\desktop
感謝您的教學,很仔細,可以幫助大家更好喔!
嗯嗯!一起讓世界成為更好的地方吧~
非常實用!!謝謝分享!!
謝謝你的肯定!
請問一下批次檔有辦法執行去複製A.txt裡帶著ABC的文字.到另一個B.txt裡嗎?
當然可以喔! 宏觀上來看,首先你要將 A.txt 裡每一行字個別讀取出來,所以會需要用到FOR。接下來,你想對每一行字做檢查,看是否含有特定的文字片段,所以會需要常規表示式(regex)。 以下為參考code: @ echo off for /f %%L in (A.txt) do ( echo %%L | findstr /r "ABC" >> b.txt )
想請問您,批次檔是否能做到將檔名大寫改小寫的部分,網上找各種資料都無法達到目的..
https://stackoverflow.com/questions/34713621/batch-converting-variable-to-uppercase 推薦他解答的第二種方法 (使用WSH) 我仿照他,寫了以下code,它會把當前位置的所有txt檔名轉成大寫,然後印出來給你看 (記得把 @ 改成半形喔) @if (@CodeSection == @Batch) @then @echo off & setlocal rem # For each txt file for %%F in (*.txt) do ( rem # Change to uppercase and print it out for /f "delims=" %%I in ('cscript /nologo /e:JScript "%~f0" "%%F"') do ( echo %%~I ) ) goto :EOF @end WSH.Echo(WSH.Arguments(0).toUpperCase());
你好,請教一下批次檔是否能做到將檔案從C:\ 複製到D:\,但我複製過來的檔名要自動帶上今天的日期可以做到嗎 ?
當然做得到阿! 像這一類有關 "如何做" 的問題,其實只要用英文關鍵字一查,就有很豐富的資料了~ (我是用 "cmd get date time" 找到資料的) 重點在於看得懂英文,以及下對關鍵字。 for /f "tokens=1-3 delims=/ " %%a in ("%date%") do (set mydate=%%a-%%b-%%c) for /f "tokens=1-3 delims=:. " %%a in ("%time%") do (set mytime=%%a-%%b-%%c) 第一行會得到像 2018-12-7 的資料,存在 mydate 變數中。第二行會得到時間 13-15-22。 如果你只想留下月份和日期,不想要年份,就把第一行後面的 %%a 移除掉,然後前面範圍改成 2-3。 for %%f in (*.jpg) do (echo %%~nf_%mydate%.jpg) 這一行會把所有的 .jpg 檔印出來到螢幕上,並且名字最後都會加上 "_2018-12-7"。 如果你最後想要的是複製,那麼就把 echo 指令改成 copy %%f,並且後面接原本那一串。 參考: 參考Code: https://ideone.com/h5hwpv https://stackoverflow.com/questions/203090/how-do-i-get-current-datetime-on-the-windows-command-line-in-a-suitable-format https://stackoverflow.com/questions/3215501/batch-remove-file-extension
您好 我請教您 我目前的語法是這樣 FOR /F "tokens=1-4 delims=/ " %%a IN ("%date%") DO SET CUR-DRB=%%a-%%b-%%c XCOPY "Y:\" "E:\bill\%CUR-DRB%" /D/K/Y/ XCOPY "Y:\" "E:\bill" /Y/R/M 可是他開機進桌面執行後 他會出現一個需要我KEY (F=檔案,D=目錄)? 這行文字 請問有沒有辦法加入指令直接鍵入D讓他執行完畢結束呢??
乍看之下,因為它要你輸入 F=檔案 D=目錄,所以跟 for 無關,是 xcopy 的問題。 你的問題應該改成,如何讓 xcopy 不要產生提示 “prompt”,這可以拿去爬文,可能就會找到解答了。 像是官網上面就有寫 https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/xcopy /i If Source is a directory or contains wildcards and Destination does not exist, xcopy assumes Destination specifies a directory name and creates a new directory. Then, xcopy copies all specified files into the new directory. By default, xcopy prompts you to specify whether Destination is a file or a directory.
請問如何新增一個檔案,檔名自動命名為該資料夾的名稱呢?(假設為在LOVE資料夾下新增.txt好了)謝謝 一直找不到適合的寫法,感恩
你是一次有好幾個資料夾嗎? 如果你只有一個資料夾,還不如直接手動操作比較快XD 假設你一次有好幾個資料夾好了,譬如說是 LOVE1, LOVE2, LOVE3,...,那你加上這行就可以了: for /D %%i in (LOVE*) do (echo 123 > %%i/%%i.txt) for /D 會跑遍所有的"資料夾",請自行讀使用手冊(https://ss64.com/nt/for.html)。echo 加上 > 是重新導向,我的文中有說明,所以新建的txt檔裡面都會含有"123"。
感恩~從您提供的解法中得到許多靈感^^
你好, 我自己不會寫bat,每次使用時總要查很多指令,才能拼湊出來,我想問目前遇到的問題.. 1. 變數 年、月的寫法 @echo off set hour=%time: =0% set File=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2% echo %File% xcopy "D:\共用\倉儲共用\1_理貨.庫存\飛比\109年比菲多\過允收回廠單\*1月.xlsx" "D:\0_自訂表單\Backup\倉儲共用%File%\飛比\" /D /Y a) 年度想改為變數,例109年(台灣年度)及西元年(2020年),要如何改為變數? b) 月份的變數寫法,例:9月、12月.....我查了很多資料,月份只有個位數時,都會自動補"0",要怎麼寫,月份變數才會成為1,2,3...12 ?? 2. 把excel檔案,按存檔的那一刻,把A1儲存格內容Delete,但不改變儲存格格式,可以用bat檔做到嗎?
你好, a) 我不太理解您具體想改變的是什麼?是想把西元年轉成民國年嗎?還是想達成什麼樣的目標? b) 如果它會自動補 0,你就寫程式把 0 去掉阿。 set month=%DATE:~5,2% if %month:~0,1% == 0 (set month=%month:~1%) 子字串的語法是這樣的: %Variable:~Position,Length%。 2) 這應該是 Excel 的層次,可能要去往 excel macro 查詢。或者可能要思考有沒有更簡單的解決方法。
謝謝你的回覆,果然bat無法與excel搭配
我單純以以下指令建立了bat檔 但建立好後 點兩下後無法執行? @echo on for %d in (c d e f g h i j k) do cacls "%d:\System Volume Information" /e /g everyone:f for %d in (c d e f g h i j k) do rd/q/s "%d:\System Volume Information" for %d in (c d e f g h i j) do cacls "%d:\$Recycle.Bin" /e /g everyone:f for %d in (c d e f g h i j) do rd/q/s "%d:\$Recycle.Bin" for %d in (c) do cacls "%d:\Documents and Settings" /e /g everyone:f for %d in (c ) do rd/q/s "%d:\Documents and Settings" ECHO finish
照理說,如果你的電腦是Windows,雙擊.bat檔就會自動執行了。因為通常是看不到執行畫面的,所以你的程式應該是已經執行完了只是可能執行結果失敗。建議你仔細研究一下程式哪部份出錯了(debug)。
你好我的指令如下: ECHO off ffmpeg -headers "Origin: 123.com" -i "456.com" -c copy "輸出的檔案名稱.ts" 請問: 1.如果想要每次執行.bat檔時,就詢問 "456.com" 中的 " " 要填入甚麼,如789.com,要如何撰寫? 2.如果想要每次執行.bat檔時,就詢問 "輸出的檔案名稱.ts" 中的 " " 要填入甚麼,如 01.ts ,要如何撰寫?
你可以: set /p in="輸入的名稱" set /p out="輸出名稱: " 然後再: ffmpeg -i "%in%" -c copy "%out%.ts"
不好意思 我是一個新手 想請問一下 如果多使用者PROFILE 該怎麼寫 複製貼上的指令呢? C:\Users\XXX\AppData\LocalLow\Sun\Java\Deployment\security XXX=會有不同的使用者
這樣呢? cd C:\Users for /d %%i in (*) do ( echo "C:\Users\%%i\AppData\LocalLow\Sun\Java\Deployment\security" )
你好~ 請問 findstr /r 這個/r有什麼作用呢?
就是正規表示式的作用喔 https://docs.microsoft.com/zh-tw/windows-server/administration/windows-commands/findstr