那個批次檔,該遷移到 PowerShell 嗎? ── cmd/bat 資產盤點與遷移判斷

· · PowerShell, Windows, 批次檔, cmd, VBScript, 既有資產活用, 維運改善, 腳本

「伺服器遷移的時候,冒出了一大堆用了 20 年的批次檔。這些是不是應該全部改寫成 PowerShell?」── 在舊有資產的諮詢中,這個問題可以說是固定戲碼。複製與備份、應用程式啟動的包裝器、夜間的資料連動作業。cmd 的批次檔(bat)至今仍在悄悄支撐著 Windows 的維運現場。

先講結論:不需要全部改寫。bat 目前並沒有馬上停止運作的計畫,而且把正在運作中的東西拿去改寫,這件事本身就是故障風險。但另一方面,確實存在「原封不動」很危險的 bat:呼叫了 VBScript 的、把錯誤吞掉的、今後會有變更的。不做這樣的判斷區分,不論是「全部保留」還是「全部遷移」,都容易釀成事故。

本文以中小企業的資訊系統或維運負責人為對象,整理 bat 資產的盤點與遷移判斷基準、bat 特有的陷阱、過渡期讓 bat 與 PowerShell 共存的寫法,以及改寫對照表。

1. 先講結論

  • cmd.exe 與 bat 沒有廢止計畫,但 Microsoft 明確表示 Windows 自動化建議使用 PowerShell。若是要新寫的,官方方向就是 PowerShell。1
  • VBScript 已於 2023 年 10 月正式宣布非建議使用,官方已公開先成為隨需安裝功能(Feature on Demand)、之後再從作業系統中移除的分階段計畫。從 bat 以 cscript / wscript 呼叫 VBScript 的資產,是遷移必要度最高的一群。23 官方提供的替代方案是 PowerShell。4
  • 遷移判斷不是「全部」,而是分類。穩定運作中且無變更計畫 → 保留。有變更、需要錯誤處理、需要記錄檔 → 遷移。依賴 VBScript → 優先遷移。第 4 章的判斷表將整理這些。
  • bat 在結構上的弱點,是錯誤不會中斷執行、%ERRORLEVEL% 的陷阱,以及文字編碼。if errorlevel 1 是「1 以上」的判斷5,%errorlevel% 若被同名環境變數定義就會壞掉。5
  • 遷移到 PowerShell 可以得到的,是物件管線、以 try/catch 做的錯誤處理6、以 -WhatIf 做的預演7,以及 Pester 測試。「能察覺失敗、能安全試跑、能自動測試」正是遷移的本質價值。
  • 過渡期的現實解,是從 bat 以 -File 呼叫 powershell.exe / pwsh。腳本 exit 回傳的結束碼會原封不動傳到 bat 端的 %ERRORLEVEL%,因此可以在維持既有作業管理的情況下,只替換內容。89
  • 像 robocopy 這類優秀的外部指令不要改寫,直接從 PowerShell 呼叫即可。結束碼是 0~8 以上的獨特體系,8 以上代表失敗,因此只需正確地用 $LASTEXITCODE 做判斷。1011
  • 發布時要把執行原則納入設計。Windows PowerShell 5.1 用戶端作業系統的預設值是 Restricted(無法執行腳本),Windows Server 與 PowerShell 7 則是 RemoteSigned。即使是 RemoteSigned,帶有網際網路來源標記且未簽章的腳本仍會被封鎖。12

2. 為什麼是現在 ── cmd 會留下、VBScript 會消失

首先整理前提事實。Windows 命令列殼層有 cmd(命令殼層)與 PowerShell 兩套系統,Microsoft 官方文件明確指出「最堅固、最新的 Windows 自動化,建議使用 PowerShell,而非 Windows 命令或 Windows Script Host」。1 不過建議終究只是建議,cmd 本身並未列在 Windows 的非建議使用功能清單中。目前並沒有既有 bat 會停止運作的計畫。

與此形成對比的是 VBScript。2023 年 10 月,VBScript 正式宣布非建議使用。官方已公開計畫:未來的 Windows 版本將以隨需安裝功能(Feature on Demand)提供,之後再從作業系統中移除。2 一開始會以預先安裝的狀態提供,所以並不是今天明天就會停止3,但「移除」這個方向已經確定。Windows Server 2025 同樣已 FoD 化,官方也提供了以 PowerShell 作為替代方案的說明。4

關於時間點,在此正確地寫明。截至 2026 年 7 月,Microsoft 官方文件所寫的只是「先以 FoD 提供,再於未來的 Windows 版本中移除」這個順序,並未標示移除的年月。23 也就是說,「還有幾年」這個問題,官方目前無法回答。這並不代表「還早,可以放著不管」,而是等到公布期限之後才開始調查,會來不及。只要先把盤點(第 7 章)做完,一旦公布了移除時間,就能立刻回答「會受影響的是這 3 支」。

這裡重要的是,bat 資產裡藏著 VBScript。從 bat 以 cscript //nologo convert.vbs 這樣的方式呼叫 VBScript,是 2000 年代常見的固定寫法。這種結構的 bat 並不是「因為是 bat 所以安全」,而是「與 VBScript 共存亡」。盤點時,除了批次檔本身,也務必確認 bat 呼叫的對象是什麼(第 7 章會提供掃描用腳本)。針對企業內部 VBScript、VBA 資產整體的檢查,詳見「為 VBScript 停用做準備:VBA・企業內部工具盤點指南」。

3. bat 的陷阱,以及 PowerShell 能帶來的東西

作為遷移判斷的材料,具體來看看 bat 在結構上的弱點。以下批次檔是常見的備份處理寫法。

@echo off
rem 夜間備份(常見卻危險的批次檔)
xcopy C:\data \\backup01\share\data /E /Y
echo 備份完成 >> C:\logs\backup.log

這支批次檔裡,埋著 3 個不容易察覺的問題。

  • 就算發生錯誤也不會中斷。即使 xcopy 因無法連上共用目的地而失敗,cmd 預設仍會繼續往下一行,記錄檔中還是會寫入「備份完成」。要偵測失敗,每次都得自行撰寫 if errorlevel 的判斷。
  • %ERRORLEVEL% 有陷阱。if errorlevel 1 並非「結束碼等於 1」,而是「1 以上」的判斷。5 另外,%errorlevel% 這種寫法,規格上是「若沒有定義名為 ERRORLEVEL 的環境變數,就展開為目前的結束碼」,因此一旦有人寫了 set ERRORLEVEL=0,之後的判斷就全部壞掉。5 再加上 exit /b 數值 回傳結束碼的規格13,要正確寫對需要相當的知識。
  • 文字編碼的事故。日文環境下的 cmd 傳統上屬於 Shift_JIS 系的世界,把用 UTF-8 重新儲存的 bat 拿去執行就出現亂碼、含符號的路徑導致誤動作,這類事故至今仍在發生。這個問題的全貌,請參考「整理 Windows 的字元編碼與換行符 - Shift_JIS / UTF-8 / UTF-16、亂碼、CRLF / LF,為何混亂」。

用 PowerShell 寫同樣的處理,失敗的處理方式會有結構性的改變。

# 夜間備份(PowerShell 版骨架)
$ErrorActionPreference = 'Stop'   # 讓錯誤傾向於「中斷」
try {
    # 複製來源要指定「資料夾內容」的 C:\data\*。若傳入 'C:\data',
    # 第二次以後複製目的地資料夾已經存在時,會巢狀變成 data\data
    Copy-Item -Path 'C:\data\*' -Destination '\\backup01\share\data' -Recurse -Force
    Add-Content -Path 'C:\logs\backup.log' -Value "$(Get-Date -Format o) 備份完成"
    exit 0
}
catch {
    # 可以將「哪裡、發生了什麼」以結構化資訊的形式保留記錄
    Add-Content -Path 'C:\logs\backup.log' -Value "$(Get-Date -Format o) 失敗: $($_.Exception.Message)"
    exit 1
}

try/catch 可以捕捉會終止陳述式的錯誤,善後處理則可以寫在 finally 裡。6 此外,PowerShell 還提供了共通機制 -WhatIf,能在不執行破壞性操作的情況下,只顯示「原本會發生什麼」7,可以針對改寫後的腳本,對正式環境安全地做預演。

-WhatIf 光用文字說明很難有實感,這裡列出實際的呈現方式。官方文件的範例中,即使執行 Remove-Item Date.csv -WhatIf,檔案也不會被刪除,只會顯示以下這一行。7

What if: Performing operation "Remove File" on Target "C:\ps-test\date.csv".

每個對象會顯示一行,所以像 Remove-Item C:\logs\*.log -WhatIf 這樣用萬用字元寫的情況下,即將被刪除的檔案會在執行前全部列出來。也就是說,可以在不破壞正式資料的情況下,確認「這個條件下真的只會命中想要的目標嗎」(顯示訊息使用的語言依環境設定而定)。bat 沒有對應的機制,只能靠用 echo 組出指令、用肉眼檢查這種手動方式。

再加上用 Pester 寫測試,就能持續自動確認「應該正常運作」是否成立(參見「用 Pester 整備 PowerShell 測試 ── 讓維運腳本不易損壞的實務做法」)。錯誤處理與重試設計本身,則在同時發布的「PowerShell 的錯誤處理與重試設計」中有更深入的說明。

也就是說,遷移能帶來的並非語法的新穎,而是能察覺失敗、能安全試跑、能靠測試守護這種維運品質。反過來說,對於一旦失敗就能馬上察覺的單純啟動包裝器類 bat,這種價值不太能發揮效果。所以才需要先做分類。

4. 不要全部遷移 ── 分類判斷表

論點 選項(在推薦選項上明記「推薦」) 判斷基準
穩定運作中・無變更計畫・單純 保持原樣(推薦) / 遷移 應用程式啟動包裝器或幾行的複製指令可以保留。改寫本身就是新風險
有呼叫 VBScript(cscript/wscript) 保留 / 優先遷移(推薦) VBScript 已由官方公布非建議使用 → FoD → 移除的計畫。放著不管,終究會迎來廢止之日2
今後會有規格變更・新增功能 維持 bat 進行修改 / 先遷移再修改(推薦) 變更的時機正是遷移的好機會。修改與測試整備可以同時進行
需要偵測失敗・記錄檔・重試 在 bat 上補判斷 / 遷移(推薦) 要窮舉 if errorlevel 難以維護。是 try/catch 與結構化記錄能發揮效果的領域6
夜間作業(工作排程器啟動) 維持現狀 / 考慮遷移(推薦) 無人執行時,失敗的處理方式格外重要。也應一併重新檢視執行帳戶,以及「以 0x1 結束」的問題(第 7 章後述)
以 robocopy 等外部指令為主 用 Cmdlet 重新實作 / 保留外部指令並直接呼叫(推薦) 重新實作有實績的指令得不償失。只把呼叫與判斷改成 PowerShell10
撰寫者已離職・規格不明 不碰 / 先記錄行為再判斷(推薦) 先將現有的輸入輸出與排程文件化。在不明的狀態下不要改寫

猶豫時的原則有兩個。第一,遷移是依「產生價值的順序」,而非「年代久遠的順序」。第二,若要動手,先從讀取(盤點與記錄)開始

5. 過渡期的現實解 ── 從 bat 呼叫 PowerShell,並傳遞結束碼

分類的結果,許多現場會進入「一部分維持 bat、一部分改為 PowerShell」的過渡期。這時候方便的做法,是將 bat 保留為入口(與作業管理的接點),只把內容改成 PowerShell。作業排程器與維運手冊會持續看著 bat 的路徑,因此可以在不破壞周邊的情況下,將內容現代化。

@echo off
rem 入口 bat:呼叫同一資料夾的 job.ps1,並傳遞結束碼
rem %~dp0 是這支 bat 自身所在的資料夾(官方文件也記載的常規寫法)
rem 若殘留了有人執行過 set ERRORLEVEL=... 的環境變數,%ERRORLEVEL%
rem 就會隱藏真正的結束碼,因此呼叫前先清除,回復為動態值
rem 這裡不要覆寫執行原則。指定 -ExecutionPolicy 會產生優先度較高的
rem Process 範圍,悄悄削弱管理員設定的 AllSigned 等原則
set "ERRORLEVEL="
powershell.exe -NoProfile -File "%~dp0job.ps1"
exit /b %ERRORLEVEL%

不依賴目前工作目錄,而以 %~dp0 解析腳本位置的寫法,正是官方文件中示範的「從 bat 呼叫」範例。9 若要用 PowerShell 7 執行,只要把 powershell.exe 換成 pwsh 即可(5.1 與 7 是分開安裝、可以共存的。如何區分使用,請參考同時發布的「Windows PowerShell 5.1 與 PowerShell 7 的差異與遷移」)。

結束碼的傳遞方式可以整理如下。

  • 若腳本以 exit 4 結束,行程的結束碼就會是 4,bat 端可以用 %ERRORLEVEL% 接收。這種連動方式,官方文件附有實例記載。8
  • -File 呼叫時,若腳本因未處理的例外而終止,結束碼為 1;正常結束則為 0。為了避免「明明失敗卻回傳 0」,腳本端應自行判斷成敗並明確 exit,這樣比較安全。914
  • 從 PowerShell 呼叫外部指令的結果,會存入自動變數 $LASTEXITCODE11

以 robocopy 為主角的 bat,正是這種形式的好例子。robocopy 具備重試(預設竟高達 100 萬次、等待 30 秒)、鏡像、記錄檔等有實績的功能10,沒有理由用 Copy-Item 重新實作。不過結束碼的體系很獨特,0 代表「沒有複製對象」,1 代表「正常複製」,8 以上才代表失敗10 若用「非 0 即異常」這種一般規則判斷,會把正常的複製誤判為異常。

# robocopy 直接使用,只把判斷改用 PowerShell 正確地寫
# /r:2 /w:5 ── 預設的重試 100 萬次,在無人作業中實質上等於掛起,務必收窄
robocopy 'C:\data' '\\backup01\share\data' /MIR /R:2 /W:5 /NP /LOG+:'C:\logs\robocopy.log'

if ($LASTEXITCODE -ge 8) {
    Write-Error "robocopy 失敗了(結束碼: $LASTEXITCODE)"
    exit 1
}
Write-Host "同步完成(結束碼: $LASTEXITCODE)"  # 0~7 屬於成功系列
exit 0

6. 改寫對照表 ── 把 bat 的詞彙換成 PowerShell

實際改寫時的對照表。請不要當作機械式的替換,而是當成「同樣的意圖要如何表達」的對照表來使用。

bat 的寫法 PowerShell 的常規做法 補充
copy / move / del / md Copy-Item / Move-Item / Remove-Item / New-Item 破壞性操作先加上 -WhatIf 做預演7
xcopy / robocopy 直接呼叫 robocopy 不要重新實作。用 $LASTEXITCODE 判斷 8 以上為失敗1011
for %%f in (*.csv) do ... Get-ChildItem *.csv \| ForEach-Object { ... } 流動的不是檔名字串,而是物件
if errorlevel 1 goto :error try/catch + $ErrorActionPreference = 'Stop' 外部指令仍照舊用結束碼判斷6
set VAR=value $var = 'value'(環境變數用 $env:VAR) 能區分行程環境變數與變數
call :sub / goto function 可以為參數加上型別與驗證
findstr Select-String 相符的行以物件形式回傳,可在後段加工
>> log.txt Start-Transcript / Add-Content 若要整體採集執行記錄,Transcript 較方便
rem #

PowerShell 端的基本詞彙(如何尋找 Cmdlet、管線、確認的作法),整理在「PowerShell 指令基礎 ── 該先學會的操作與安全使用方式」中。

7. 分階段遷移的步驟 ── 盤點 → 分類 → 試點 → 並行運作

最後,把進行方式整理成 4 個階段。

(1) 盤點。首先從唯讀調查開始。以機械方式全面找出 bat 的所在位置,以及是否依賴 VBScript。

# bat 資產盤點:所在位置清單,以及偵測 VBScript 呼叫(只做讀取的安全調查)
# 無法列舉到的位置會成為盤點的漏洞,因此用 -ErrorVariable 記錄下來,之後再揭露
$targets = Get-ChildItem -Path 'C:\', 'D:\jobs', '\\fileserver\scripts' -Recurse `
    -Include '*.bat', '*.cmd' -File -ErrorAction SilentlyContinue -ErrorVariable enumErrors

$readErrors = @()
$report = foreach ($file in $targets) {
    # 若有呼叫 cscript/wscript/.vbs,就標記為「依賴 VBScript」的注意旗標
    # 已列舉出的路徑用 -LiteralPath 傳遞(若把像 [2026]job.bat 這種含中括號的
    # 檔名傳給 -Path,會被解讀為萬用字元,結果看到別的檔案)
    # 因鎖定等原因而無法讀取內容的檔案,也會記錄在 $readErrors 中
    $vbs = Select-String -LiteralPath $file.FullName -Pattern 'cscript|wscript|\.vbs' -Quiet `
        -ErrorAction SilentlyContinue -ErrorVariable +readErrors
    [pscustomobject]@{
        Path          = $file.FullName
        LastWriteTime = $file.LastWriteTime
        UsesVBScript  = $vbs
    }
}
$report | Sort-Object UsesVBScript -Descending | Export-Csv 'C:\audit\bat-inventory.csv' -NoTypeInformation -Encoding UTF8

# 把調查不到的位置留存為清單(若不是空的,表示盤點並不完整)
# 同時包含列舉失敗的位置,以及雖已列舉但無法讀取內容的檔案
@($enumErrors) + @($readErrors) | ForEach-Object { $_.TargetObject } |
    Set-Content -Path 'C:\audit\bat-uninspected.txt'

產生出來的 bat-inventory.csv 是只有 3 欄的樸素表格。UsesVBScriptTrue 的列,就是優先遷移的候選,經過 Sort-Object 後會排在最前面。內容大致如下(路徑與時間僅為範例)。

Path LastWriteTime UsesVBScript
\\fileserver\scripts\nightly\convert.bat 2009/04/13 18:22:31 True
D:\jobs\eod\export_shipping.bat 2014/11/07 9:41:02 True
D:\jobs\backup\copy_master.bat 2021/06/02 14:05:47 (空白)
C:\tools\launch_viewer.bat 2018/02/19 11:30:15 (空白)

UsesVBScript 欄位變成空白並不是異常。這是因為 Select-String-Quiet,規格上是「相符時回傳 $true,不相符時不是回傳 $false,而是回傳 $null」。15 若只用「True 還是空白」來排序,實務上不會有問題,但若想明確標示為 False,請像 $vbs = [bool](Select-String ...) 這樣轉換成布林值後再存入(時間顯示格式依執行環境的地區設定而定)。

光是這 4 行,判斷就能大幅推進。前 2 支屬於第 4 章的「優先遷移」,第 3 支是「若有變更計畫就遷移」,最後的啟動包裝器則是「保持原樣」。實務上會在這裡手動再補上啟動來源(工作排程器/作業管理工具/人工)與負責人這兩欄,直接當作遷移計畫的台帳來用。LastWriteTime 在 10 年以上的,可視為「撰寫者已經不在了」的候選,先預留判讀的時間會比較安全。

(2) 風險分類。將盤點結果套入第 4 章的判斷表,分類為「保留」「遷移」「優先遷移(依賴 VBScript)」。同時記錄各 bat 的啟動來源(工作排程器、作業管理工具、人工)與失敗時的影響範圍。這裡要先掌握的是「以 0x1 結束」的問題。工作的「上次執行結果」中出現的 0x1,意思並非工作排程器本身的錯誤,而是被啟動的程式回傳了結束碼 1,原因幾乎都出在「手動執行與定期執行的環境差異」上。典型模式是:因為工作端沒有指定「開始位置(選用)」,導致目前工作目錄變成 C:\Windows\System32,以相對路徑為前提的 bat 因此壞掉。其他原因還包括:設定檔來源的環境變數或已對應的網路磁碟機,在無人工作階段中不存在,以及執行原則不同等。釐清問題的步驟,請參考「工作排程器的工作不執行、以 0x1 結束 ── 原因排查與安全的維運設計」。

(3) 試點。選出一支影響較小的批次檔,以第 5 章的入口 bat 方式改寫。此時要把執行原則納入設計。Windows 的預設值是 RemoteSigned,本機建立的腳本可以運作,但帶有網際網路來源標記且未簽章的腳本會被封鎖。12 若透過共用資料夾發布時遇到卡關的釐清方法,以及包含簽章運作在內的正規設計,整理在同時發布的「PowerShell 的執行原則與腳本簽章」中。

(4) 並行運作。讓舊 bat 與新腳本並行運作一段期間。分開寫入目的地並比對輸出、新的一方先只用 -WhatIf 或只寫記錄檔來運作、回退時只要把作業的指向改回 bat 即可 ── 把這些都準備好之後再讓舊 bat 退役,遷移本身幾乎就不會變成事故。

8. 總結

  • cmd 與 bat 沒有廢止計畫,但官方建議是 PowerShell。另一方面,VBScript 已公布非建議使用 → FoD → 移除的計畫,從 bat 呼叫的 VBScript 是最優先的遷移對象。
  • 遷移不是「全部」,而是分類。穩定運作、無變更的 bat 保留,有變更的、需要錯誤處理與記錄檔的、依賴 VBScript 的,則從這些開始遷移。
  • bat 遇到錯誤不會中斷,if errorlevel 1 是 1 以上的判斷,%errorlevel% 會因同名環境變數而壞掉。PowerShell 則以 try/catch・-WhatIf・Pester 提供「能察覺、能試跑、能守護」。
  • 過渡期的現實解,是從入口 bat 呼叫 powershell.exe -File(或 pwsh -File),並以 exit%ERRORLEVEL% 傳遞結束碼。
  • 像 robocopy 這類優秀的外部指令不要改寫,只需正確地用 $LASTEXITCODE 判斷(8 以上為失敗)。
  • 步驟是盤點(從讀取開始)→ 風險分類 → 試點 → 並行運作。也別忘了執行原則與發布的設計。

相關文章

相關諮詢領域

合同會社小村軟體承接 bat・VBScript・PowerShell 混雜的舊有資產盤點與遷移計畫擬定、夜間作業的 PowerShell 化與維運設計,以及遷移伴隨的故障調查。即使是「撰寫者已經不在了」的批次檔解讀,也歡迎諮詢。

參考連結

  1. Microsoft Learn, Windows Commands. 關於 Windows 具有命令殼層(cmd)與 PowerShell 兩種殼層,以及「最堅固、最新的 Windows 自動化建議使用 PowerShell,而非 Windows 命令或 Windows Script Host」這項官方記述。  2

  2. Microsoft Learn, Deprecated features for Windows client. 關於 VBScript 的非建議使用已於 2023 年 10 月宣布,以及未來的 Windows 版本將先以 Feature on Demand 提供、之後再從作業系統中移除的計畫。  2 3 4

  3. Microsoft Learn, Resources for deprecated features. 關於 VBScript 的 Feature on Demand 一開始會以預先安裝的狀態提供,在退役準備期間可以不中斷地使用。  2 3

  4. Microsoft Learn, Features removed or no longer developed in Windows Server. 關於 Windows Server 2025 中 VBScript 已以 FoD 提供、並將在之後的版本中移除,以及官方已提供以 PowerShell 作為工作自動化與腳本替代方案的說明。  2

  5. Microsoft Learn, if. 關於 if errorlevel 是「上一個程式的結束碼為 number 以上時為真」的以上判斷,以及 %errorlevel% 是以不存在名為 ERRORLEVEL 的環境變數為前提的展開,若定義了同名環境變數則會回傳該值。  2 3 4

  6. Microsoft Learn, about_Try_Catch_Finally. 關於 try/catch/finally 區塊會處理陳述式終止錯誤與腳本終止錯誤,catch 可以指定例外型別,finally 無論是否發生錯誤都會執行、可用於善後處理。  2 3 4

  7. Microsoft Learn, about_CommonParameters. 關於會變更系統或資料的 Cmdlet 所提供的風險緩解參數(-WhatIf/-Confirm),以及 -WhatIf 不會實際執行指令,只會顯示影響說明。  2 3 4

  8. Microsoft Learn, about_Language_Keywords. 關於 exit 關鍵字會設定 $LASTEXITCODE,在 cmd.exe 端則對應 %ERRORLEVEL%,以及用 pwsh -File 執行的腳本中 exit 4 會在呼叫端的 %ERRORLEVEL% 觀察到 4 的實例,還有沒有 exit 陳述式時,正常結束為 0、未處理例外為 1。  2

  9. Microsoft Learn, about_Pwsh. 關於 pwsh 的 -File 參數用法、從批次腳本呼叫時以 %~dp0 表示執行目錄的官方範例、以 -File 執行時若發生腳本終止錯誤結束碼會是 1,以及若以 exit 指令結束,則該數值即為結束碼。  2 3

  10. Microsoft Learn, robocopy. 關於 robocopy 的結束碼體系(0=沒有複製對象、1=所有檔案正常複製、8 以上=1 件以上失敗)、重試預設值為 /r:1000000(100 萬次)・等待預設值為 /w:30 秒,以及鏡像與記錄檔等選項。  2 3 4 5

  11. Microsoft Learn, about_Automatic_Variables. 關於自動變數 $LASTEXITCODE 會存放原生程式或腳本的結束碼,以及用 -File 執行腳本時該值的決定方式(例外為 1、exit 指定值、正常結束為 0)。  2 3

  12. Microsoft Learn, about_Execution_Policies. 關於 Windows 的預設執行原則為 RemoteSigned,RemoteSigned 會要求來自網際網路的腳本具備受信任發行者的簽章、但不要求本機建立的腳本簽章,以及各原則(Restricted/AllSigned/Bypass 等)的意義。  2

  13. Microsoft Learn, exit. 關於 exit /b 會結束批次腳本並將指定值設定給 ERRORLEVEL 環境變數,若結束的是 cmd 本身,則會設定為行程的結束碼。 

  14. Microsoft Learn, about_PowerShell_exe. 關於 Windows PowerShell 5.1 的 powershell.exe 中 -File 參數的規格、從 cmd.exe 傳遞環境變數時的 %windir% 語法,以及結束碼的處理方式。 

  15. Microsoft Learn, Select-String. 關於 Select-String 是以正規表示式搜尋文字的 Cmdlet,以及指定 -Quiet 時的回傳值不是 MatchInfo 物件,而是找到模式時為 $true、找不到時為 $null。 

共用相同標籤的最新文章。能以相近的主題延伸理解。

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

本文連結到以下服務頁面,歡迎從最接近的入口查看。

常見問題

整理諮詢這個主題時常見的問題。

企業內部的批次檔應該全部遷移到 PowerShell 嗎?
不建議全部遷移。已穩定運作多年且沒有變更計畫的批次檔,改寫本身就會成為新的故障風險。優先度高的遷移對象,是今後會有變更的、需要錯誤處理或記錄檔的,以及呼叫了 VBScript(cscript/wscript)的批次檔。用判斷表分類,從有價值的部分開始分階段遷移,才是實務上的常規做法。
cmd.exe 或批次檔會被廢止嗎?
cmd.exe 並未列在 Windows 的非建議使用功能清單中,也沒有廢止的公告。不過 Microsoft 明確表示,Windows 自動化建議使用 PowerShell,而非命令或 WSH。另一方面,VBScript 已於 2023 年 10 月正式宣布非建議使用,未來的 Windows 將先成為隨需安裝功能(Feature on Demand),之後再從作業系統中移除,官方已公開這個分階段計畫。bat 本身會繼續運作,但 bat 呼叫的 VBScript,遲早會有無法運作的一天。
要如何從 bat 檔案呼叫 PowerShell 腳本?
用 -File 參數,將腳本傳給 powershell.exe(或 PowerShell 7 的 pwsh.exe)。若腳本與 bat 位於同一資料夾,官方文件中也記載了用 %~dp0 解析路徑的寫法,例如「powershell.exe -NoProfile -File "%~dp0job.ps1"」。另外,指定 -ExecutionPolicy 會產生優先度較高的 Process 範圍,反而會削弱管理員所設定的原則,因此 bat 端不加這個參數,遵循環境端的執行原則設計即可。只要腳本端以 exit 數值回傳結果,就會原封不動地傳到 bat 端的 %ERRORLEVEL%,因此可以在維持既有作業管理機制的情況下,只將內容 PowerShell 化。
bat 的 %ERRORLEVEL% 有什麼陷阱?
最具代表性的是「if errorlevel 1」是「結束碼為 1 以上時為真」的以上判斷(並非等值判斷)。另外 %errorlevel% 是以環境變數 ERRORLEVEL 尚未被定義為前提的動態展開,若像 set ERRORLEVEL=0 這樣自行定義,之後就會一直回傳該值。此外 bat 在指令失敗時預設仍會繼續往下一行執行,因此忘記寫判斷的失敗會被靜靜地吞掉。這類陷阱較少,正是遷移到具備錯誤處理能力的 PowerShell 的動機。
使用 robocopy 的批次檔要如何改寫成 PowerShell?
常規做法是不改寫。robocopy 是擁有重試、鏡像、記錄檔等實績功能的優秀外部指令,可以直接從 PowerShell 呼叫。用 Copy-Item 重新實作既費工又會降低可靠度。要注意的是結束碼的解讀方式,robocopy 會回傳 0~8 以上的值,8 以上代表失敗,1 則代表正常複製完成。PowerShell 端要看 $LASTEXITCODE,判斷「8 以上即為異常」。
發布 PowerShell 腳本時遇到執行原則卡關,該怎麼辦?
執行原則的預設值(RemoteSigned)會要求標記為來自網際網路的腳本必須具備簽章。若透過共用資料夾發布而被封鎖,首先要確認原因;至於長久之計,正規做法是採用簽章運作或以群組原則統一設定。從批次檔呼叫時加上 -ExecutionPolicy Bypass 雖然簡便,但請先確認這是否符合組織原則設計的本意,再行使用。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽