計(jì)劃+批處理FTP腳本實(shí)現(xiàn)無(wú)人值守自動(dòng)傳輸)
簡(jiǎn)介圍繞批處理命令與FTP自動(dòng)傳輸整理的Word文檔面向Windows及UNIX環(huán)境下的系統(tǒng)運(yùn)維人員和網(wǎng)絡(luò)管理員用于解決每天重復(fù)登錄、手工收發(fā)文件既繁瑣又容易出錯(cuò)的問(wèn)題。文檔以1.ftp與1.bat兩個(gè)文本的編寫(xiě)為主線(xiàn)說(shuō)明如何用 ftp -n -s 參數(shù)靜默調(diào)用腳本如何取舍二進(jìn)制與ASCII兩種傳輸模式如何用Mget與Mput完成收發(fā)切換并把批處理加入“任務(wù)計(jì)劃”按周期自動(dòng)執(zhí)行同時(shí)給出UNIX下shell腳本的寫(xiě)法與可執(zhí)行權(quán)限設(shè)置便于跨平臺(tái)對(duì)照。包內(nèi)共1個(gè)doc文件約44KB正文含腳本片段、步驟說(shuō)明與應(yīng)用案例可直接當(dāng)模板改造使用。已有2121人學(xué)習(xí)適合需要搭建定時(shí)傳輸任務(wù)、減少人工干預(yù)的初、中級(jí)運(yùn)維讀者參考。1. 兩個(gè)文本文件替代一個(gè)定時(shí)任務(wù)這套 FTP 自動(dòng)傳輸方案的定位半夜兩點(diǎn)半一臺(tái)常開(kāi)的 Windows 機(jī)器要從對(duì)端拉走當(dāng)天的數(shù)據(jù)文件第二天早上八點(diǎn)前必須到位。這個(gè)需求用「批處理命令實(shí)現(xiàn) FTP 文件的自動(dòng)傳輸」是最省事的解法一個(gè).ftp文件寫(xiě)會(huì)話(huà)命令一個(gè).bat文件切目錄并把腳本傳給ftp.exe最后交給系統(tǒng)自帶的任務(wù)計(jì)劃按周期觸發(fā)。全程不裝任何軟件運(yùn)行時(shí)就是C:\Windows\System32\ftp.exe。適合的場(chǎng)景很明確Windows 常開(kāi)、對(duì)端是 UNIX 或另一臺(tái) Windows、文件量幾十到幾百個(gè)、總量幾百 MB 以?xún)?nèi)、對(duì)端只提供標(biāo)準(zhǔn) FTP。不適合的同樣明確要斷點(diǎn)續(xù)傳、要加密鏈路、要做復(fù)雜重試和告警。下面按「腳本怎么寫(xiě) → 批處理怎么包裝 → 計(jì)劃任務(wù)怎么掛 → 哪里會(huì)翻車(chē)」拆開(kāi)說(shuō)。2. 拆 1.ftp從 open 到 bye 的九行命令每行都在決定成敗2.1 腳本文件的本質(zhì)是把鍵盤(pán)輸入重定向成文件ftp.exe本身是個(gè)交互式程序你在提示符下敲open、user、cd它讀一行執(zhí)行一行。-s:文件名做的事只有一件把標(biāo)準(zhǔn)輸入從這個(gè)文件讀而不是從鍵盤(pán)讀。所以腳本里能寫(xiě)什么完全取決于客戶(hù)端在交互模式下認(rèn)什么命令沒(méi)有額外語(yǔ)法沒(méi)有變量替換沒(méi)有 if 判斷也沒(méi)有循環(huán)。這一點(diǎn)想明白后面很多「為什么我寫(xiě)了變量它不認(rèn)」的疑問(wèn)就自然消失了。把原料里的腳本攤開(kāi)看實(shí)際長(zhǎng)這樣open 10.30.15.3 user anonymous 123456 cd data bin prompt mget *.* bye quit exitopen建立控制連接默認(rèn)端口 21如果對(duì)端改了端口就寫(xiě)成open 10.30.15.3 2121。user后面跟用戶(hù)名和密碼這一行實(shí)際對(duì)應(yīng) FTP 協(xié)議里的 USER 和 PASS 兩條命令密碼不寫(xiě)在user同一行也可以下一行單獨(dú)寫(xiě)客戶(hù)端會(huì)把它當(dāng) PASS 發(fā)出去。但要注意如果user后面只寫(xiě)了用戶(hù)名、下一行又忘了寫(xiě)密碼客戶(hù)端會(huì)當(dāng)場(chǎng)停在Password:提示上等人輸入無(wú)人值守場(chǎng)景下就是一直掛著直到超時(shí)。bye、quit、exit三個(gè)是同一個(gè)意思都是發(fā) QUIT 并斷開(kāi)。寫(xiě)一個(gè)就夠多寫(xiě)不報(bào)錯(cuò)但日志里會(huì)多出一段無(wú)意義的響應(yīng)。原料里三行都寫(xiě)了這屬于「寫(xiě)了也沒(méi)壞處但沒(méi)必要」。真正需要留意的是cd data這一行cd切的是遠(yuǎn)端目錄本地目錄要靠lcd或者批處理里的cd來(lái)處理這兩個(gè)概念在初學(xué)時(shí)最容易混。2.2 bin 與 ascii模式選錯(cuò)文件看著傳完了其實(shí)是壞的bin是binary的縮寫(xiě)對(duì)應(yīng) FTP 協(xié)議的TYPE I不寫(xiě)這一行Windows 客戶(hù)端默認(rèn)走TYPE A也就是 ASCII 模式。兩者的差別不在速度在于是否對(duì)字節(jié)做轉(zhuǎn)換。ASCII 模式會(huì)按行處理把本地的行尾轉(zhuǎn)成網(wǎng)絡(luò)標(biāo)準(zhǔn) CRLF對(duì)端再轉(zhuǎn)成自己的行尾二進(jìn)制模式逐字節(jié)搬運(yùn)一個(gè)字節(jié)都不動(dòng)。原料里有一句「如果是 Windows 之間傳去掉 bin 一行就可以了」這話(huà)得辯證看。兩臺(tái) Windows 之間傳純文本ASCII 模式確實(shí)能正常工作因?yàn)閮蛇叺男形捕际?CRLF來(lái)回轉(zhuǎn)換等于沒(méi)轉(zhuǎn)。但一旦傳的是壓縮包、可執(zhí)行文件、數(shù)據(jù)庫(kù)備份去掉bin就是災(zāi)難——ASCII 模式會(huì)把文件里恰好出現(xiàn)的0x0D、0x0A字節(jié)當(dāng)成行尾改寫(xiě)傳完大小對(duì)不上解壓報(bào) CRC 錯(cuò)誤備份文件恢復(fù)不了。更麻煩的是這種損壞不會(huì)當(dāng)場(chǎng)報(bào)錯(cuò)日志里照樣是226 Transfer complete等你去恢復(fù)數(shù)據(jù)的時(shí)候才發(fā)現(xiàn)。按文件類(lèi)型選模式的對(duì)照關(guān)系我一般按這張表來(lái)定文件類(lèi)型傳輸模式選錯(cuò)的后果.txt/.csv/.log/.sqlascii用 bin 傳UNIX 端每行末尾多個(gè)^M解析腳本讀出錯(cuò)位.zip/.rar/.7z/.gzbin用 ascii 傳壓縮包損壞解壓直接報(bào)錯(cuò).exe/.dll/.sobin用 ascii 傳可執(zhí)行文件加載失敗.dmp/.bak數(shù)據(jù)庫(kù)備份bin用 ascii 傳備份集無(wú)法恢復(fù)且不易察覺(jué).jpg/.png/.pdfbin用 ascii 傳圖片花屏、PDF 打不開(kāi)實(shí)操上的做法是按方向拆腳本而不是在一個(gè)腳本里來(lái)回切模式。收文本一個(gè).ftp收二進(jìn)制另一個(gè).ftp各自在自己那個(gè)腳本開(kāi)頭寫(xiě)死模式。混在一個(gè)腳本里也不是不行ascii和bin可以在會(huì)話(huà)中反復(fù)切換但腳本一長(zhǎng)就容易出現(xiàn)「切過(guò)去忘了切回來(lái)」的問(wèn)題排查起來(lái)很費(fèi)勁。2.3 prompt 與 mget/mput通配符的兩次展開(kāi)和確認(rèn)開(kāi)關(guān)prompt在 Windows 客戶(hù)端里是個(gè)切換開(kāi)關(guān)敲一次關(guān)掉交互確認(rèn)再敲一次打開(kāi)。關(guān)掉之后mget一次匹配到十個(gè)文件也不會(huì)逐個(gè)問(wèn)mget xxx?。不寫(xiě)這一行的后果在無(wú)人值守場(chǎng)景下很直觀任務(wù)計(jì)劃狀態(tài)一直顯示「正在運(yùn)行」日志停在一長(zhǎng)串mget a.txt?上等到超時(shí)才退出一個(gè)文件都沒(méi)傳下來(lái)。原料里把prompt放在mget前面順序是對(duì)的這個(gè)順序不能反。mget *.*里的通配符不是客戶(hù)端在本地展開(kāi)的而是原樣發(fā)給服務(wù)器由服務(wù)器的NLST或LIST來(lái)匹配。這帶來(lái)兩個(gè)邊界。第一*.*在多數(shù) UNIX 服務(wù)端只能匹配「文件名里帶點(diǎn)」的文件像README、Makefile這種不帶點(diǎn)的文件匹配不到要改成mget *。第二mget不遞歸遠(yuǎn)端data目錄下的子目錄里的文件不會(huì)被帶下來(lái)真要按層級(jí)收常見(jiàn)做法是一個(gè)子目錄寫(xiě)一個(gè)cdmget段落或者干脆在對(duì)端先打包成一個(gè)文件再傳。發(fā)送方向同理把mget換成mputcd換成遠(yuǎn)端的上傳目標(biāo)目錄本地目錄靠lcd指定。一個(gè)改好之后可直接抄的版本open 10.30.15.3 user anonymous 123456 binary prompt lcd C:\datatran cd data mget *.txt mget *.csv byelcd這一行很多示例里不寫(xiě)靠批處理里的cd來(lái)兜底。兩種做法都行但顯式寫(xiě)lcd更穩(wěn)因?yàn)榕幚淼漠?dāng)前目錄會(huì)被任務(wù)計(jì)劃的「起始于」字段影響而lcd是寫(xiě)死在腳本里的絕對(duì)路徑不受外部干擾。密碼和用戶(hù)名并到user一行也建議這么改少一行就少一個(gè)錯(cuò)位的機(jī)會(huì)。3. 1.bat 的封裝層ftp -n -s 幾個(gè)參數(shù)各自在擋什么3.1 -n 不是「不登錄」是「不要自動(dòng)匿名登錄」批處理這一層看著只有三行但ftp后面那幾個(gè)參數(shù)每一個(gè)都在解決一個(gè)具體問(wèn)題。先看原料給的寫(xiě)法C: cd datatran ftp -n -s:c:\1.ftp-n是最容易被誤解的一個(gè)。它的準(zhǔn)確含義是「啟動(dòng)時(shí)抑制自動(dòng)登錄」也就是不主動(dòng)發(fā)USER anonymous去嘗試匿名登錄。如果不加-n客戶(hù)端啟動(dòng)后會(huì)先做一次匿名登錄嘗試再執(zhí)行腳本里的open和user結(jié)果是日志里憑空多出一段 331/230 響應(yīng)某些對(duì)失敗次數(shù)敏感的服務(wù)端還會(huì)因此觸發(fā)限制。腳本里既然已經(jīng)寫(xiě)死了用戶(hù)名密碼-n就應(yīng)該加上。-s:后面緊跟腳本路徑冒號(hào)和文件名之間不能有空格這一點(diǎn)和多數(shù)命令行工具的-s 文件名寫(xiě)法不一樣。路徑里帶空格的時(shí)候用引號(hào)包住整個(gè)參數(shù)寫(xiě)成-s:C:\my scripts\1.ftp。原料用的是c:\1.ftp腳本放在盤(pán)符根目錄這個(gè)習(xí)慣在單人環(huán)境里沒(méi)問(wèn)題但放到多臺(tái)機(jī)器統(tǒng)一部署時(shí)最好歸到一個(gè)固定目錄比如C:\scripts\否則重裝系統(tǒng)或者換盤(pán)符就得挨個(gè)改。其余幾個(gè)參數(shù)按需加我整理成一張表備查參數(shù)作用什么時(shí)候必須加-n禁止啟動(dòng)時(shí)自動(dòng)匿名登錄腳本里已寫(xiě)死賬號(hào)密碼時(shí)-s:file指定命令腳本文件無(wú)人值守場(chǎng)景總是-v抑制遠(yuǎn)端響應(yīng)輸出日志要干凈、只留關(guān)鍵行時(shí)-i多文件傳輸時(shí)關(guān)閉交互確認(rèn)腳本里漏寫(xiě)prompt時(shí)的兜底-d打開(kāi)調(diào)試輸出每條 FTP 命令排查「為什么這條沒(méi)執(zhí)行」時(shí)臨時(shí)加-A強(qiáng)制匿名登錄對(duì)端只開(kāi)放匿名訪(fǎng)問(wèn)時(shí)-w:size設(shè)置接收緩沖區(qū)大小大文件吞吐調(diào)優(yōu)一般不用動(dòng)-i和腳本里的prompt是同一個(gè)效果的兩條路徑建議兩個(gè)都寫(xiě)腳本里寫(xiě)prompt是為了腳本本身獨(dú)立可讀命令行加-i是為了別人臨時(shí)改腳本時(shí)忘寫(xiě)也不至于卡死。這種「雙保險(xiǎn)」在無(wú)人值守場(chǎng)景里不算冗余算必要成本。3.2 C: 和 cd批處理的當(dāng)前目錄才是文件落地的地方C:切盤(pán)符cd datatran切目錄這兩行的作用是給后面啟動(dòng)的ftp.exe定一個(gè)當(dāng)前工作目錄。mget下載下來(lái)的文件默認(rèn)落在當(dāng)前目錄也就是這兩行切過(guò)去的C:\datatran。如果這兩行漏了ftp.exe繼承的是父進(jìn)程的當(dāng)前目錄手工雙擊 bat 的時(shí)候父進(jìn)程是資源管理器目錄可能恰好是你想要的一旦掛進(jìn)任務(wù)計(jì)劃父進(jìn)程是taskeng.exe或svchost.exe當(dāng)前目錄變成C:\Windows\System32文件全落到系統(tǒng)目錄里還因?yàn)闄?quán)限問(wèn)題寫(xiě)不進(jìn)去。更穩(wěn)的寫(xiě)法是一行到位cd /d C:\datatran/d參數(shù)讓cd同時(shí)處理盤(pán)符切換和目錄切換不用先寫(xiě)C:再寫(xiě)cd。原料里分兩行的寫(xiě)法在C:盤(pán)內(nèi)沒(méi)問(wèn)題但如果哪天目錄換到D:盤(pán)只寫(xiě)cd D:\datatran是不生效的——它只改 D 盤(pán)的當(dāng)前目錄不切當(dāng)前盤(pán)文件還是落在 C 盤(pán)。這是個(gè)很典型的老坑/d能一次性繞開(kāi)。目錄切完ftp這一行建議加上輸出重定向把會(huì)話(huà)日志留到文件里ftp -n -v -s:C:\scripts\1.ftp C:\logs\ftp_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log 21 echo [%TIME%] exit%ERRORLEVEL% C:\logs\ftp_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log是追加避免同一天多次運(yùn)行互相覆蓋21把錯(cuò)誤流也并進(jìn)同一個(gè)文件否則連接失敗的提示只會(huì)閃一下窗口就沒(méi)了。最后那行echo exit%ERRORLEVEL%很關(guān)鍵——%ERRORLEVEL%是緊跟上一條命令的退出碼中間隔了別的命令就被覆蓋了所以必須緊貼著寫(xiě)。3.3 先手工跑通再掛計(jì)劃驗(yàn)證的三個(gè)觀察點(diǎn)掛任務(wù)計(jì)劃之前一定先在命令行里手工跑一遍而且要模擬任務(wù)計(jì)劃的運(yùn)行環(huán)境不是在資源管理器里雙擊。打開(kāi)cmdcd到一個(gè)不相干的目錄再執(zhí)行C:\scripts\1.bat這樣才能暴露相對(duì)路徑的問(wèn)題。跑完之后按三個(gè)點(diǎn)檢查日志。第一看有沒(méi)有230 Login successful沒(méi)有的話(huà)是賬號(hào)、密碼或者對(duì)端權(quán)限的問(wèn)題跟腳本后面的命令無(wú)關(guān)。第二數(shù)226 Transfer complete出現(xiàn)了幾次和遠(yuǎn)端目錄里的文件數(shù)對(duì)一下少幾個(gè)就是通配符沒(méi)匹配上或者某個(gè)文件被跳過(guò)。第三比對(duì)一個(gè)具體文件的大小本地和對(duì)端一致才算真的傳完如果用的是 ASCII 模式傳二進(jìn)制文件大小可能一致但內(nèi)容已經(jīng)壞了這種情況只能靠校驗(yàn)和發(fā)現(xiàn)。%DATE%這個(gè)變量有個(gè)隱性依賴(lài)它的格式跟著系統(tǒng)區(qū)域設(shè)置走。中文系統(tǒng)默認(rèn)是2024/05/12 周日這種形態(tài)%DATE:~0,4%取到年份沒(méi)問(wèn)題換成英文系統(tǒng)就是Sun 05/12/2024同樣一段截取取到的是星期幾的縮寫(xiě)日志文件名直接亂掉。要跨機(jī)器部署的話(huà)把時(shí)間戳換成wmic os get localdatetime或者用 PowerShell 的Get-Date -Format yyyyMMdd更保險(xiǎn)別在這個(gè)細(xì)節(jié)上栽跟頭。4. 掛進(jìn)任務(wù)計(jì)劃無(wú)人值守必須擰對(duì)的三個(gè)開(kāi)關(guān)4.1 圖形向?qū)Ш?schtasks 命令行選哪個(gè)老系統(tǒng)上的路徑是「開(kāi)始 → 程序 → 附件 → 系統(tǒng)工具 → 任務(wù)計(jì)劃 → 添加任務(wù)計(jì)劃」一路下一步填周期和密碼就完事。新系統(tǒng)Win7 之后改叫「任務(wù)計(jì)劃程序」在運(yùn)行框里敲taskschd.msc打開(kāi)左側(cè)「創(chuàng)建基本任務(wù)」適合手工配一次「創(chuàng)建任務(wù)」能配的選項(xiàng)更全。圖形界面的好處是能看見(jiàn)每個(gè)開(kāi)關(guān)的中文說(shuō)明壞處是配十臺(tái)機(jī)器要點(diǎn)十遍。批量部署我一般直接用schtasksschtasks /create /tn FTP_Auto_Transfer /tr cmd /c C:\scripts\1.bat /sc daily /st 02:30 /ru SYSTEM /rl HIGHEST /f參數(shù)逐個(gè)對(duì)著看參數(shù)含義需要注意的地方/tn任務(wù)名同名會(huì)沖突配/f直接覆蓋/tr要執(zhí)行的命令路徑帶空格必須用引號(hào)建議外套一層cmd /c/sc調(diào)度周期daily / weekly / minute / onlogon / onstart/st開(kāi)始時(shí)間24 小時(shí)制 HH:mm不帶秒/mo間隔配/sc minute /mo 15表示每 15 分鐘/ru運(yùn)行賬戶(hù)填 SYSTEM 時(shí)不需要密碼/rp賬戶(hù)密碼/ru指定普通賬戶(hù)時(shí)不填會(huì)退化成「僅登錄時(shí)運(yùn)行」/rl權(quán)限級(jí)別HIGHEST 需要當(dāng)前是管理員/f同名覆蓋不加會(huì)彈交互確認(rèn)腳本里就卡住了/tr外面套cmd /c是刻意為之。任務(wù)計(jì)劃對(duì)「程序路徑」和「參數(shù)」是分開(kāi)解析的直接寫(xiě)C:\scripts\1.bat有時(shí)會(huì)被當(dāng)成程序名去找路徑里一旦有空格就斷在中間。套一層cmd /c之后整條命令交給命令解釋器處理路徑、重定向、連接符都按批處理的規(guī)則走行為更可控。4.2 「只在用戶(hù)登錄時(shí)運(yùn)行」和「不管用戶(hù)是否登錄都運(yùn)行」這一組選項(xiàng)是無(wú)人值守能不能成立的分水嶺。圖形界面里在「常規(guī)」選項(xiàng)卡頂部?jī)蓚€(gè)單選按鈕上面那個(gè)是「只在用戶(hù)登錄時(shí)運(yùn)行」下面那個(gè)是「不管用戶(hù)是否登錄都運(yùn)行」。向?qū)J(rèn)選的是上面那個(gè)因?yàn)樗恍枰婷艽a。選上面的后果是人一下班注銷(xiāo)任務(wù)就停。有些環(huán)境里用戶(hù)不注銷(xiāo)、只是鎖屏任務(wù)還能跑于是測(cè)試的時(shí)候一切正常正式跑了一周突然開(kāi)始抽風(fēng)——因?yàn)橹虚g有人重啟了機(jī)器登錄會(huì)話(huà)沒(méi)了。這種「間歇性失敗」最難查因?yàn)槭〉臅r(shí)候沒(méi)人在現(xiàn)場(chǎng)看。選下面的選項(xiàng)要輸入賬戶(hù)密碼密碼存在系統(tǒng)的憑據(jù)存儲(chǔ)里。這里有個(gè)后續(xù)麻煩域環(huán)境或者本地安全策略要求定期改密碼密碼一改任務(wù)計(jì)劃里存的舊密碼就失效任務(wù)開(kāi)始報(bào)0x8007052E登錄失敗。要么在改密碼的流程里把任務(wù)計(jì)劃一起更新要么干脆把運(yùn)行賬戶(hù)換成SYSTEM。SYSTEM的好處是不需要密碼、不受登錄會(huì)話(huà)影響、權(quán)限足夠高。代價(jià)是它看到的網(wǎng)絡(luò)環(huán)境是機(jī)器本身的不是某個(gè)登錄用戶(hù)的用戶(hù)手工映射的網(wǎng)絡(luò)驅(qū)動(dòng)器盤(pán)符它一律看不見(jiàn)必須用 UNC 路徑。純 FTP 傳輸不涉及映射盤(pán)所以SYSTEM是個(gè)很省心的選擇但如果批處理里還要往網(wǎng)絡(luò)共享目錄里搬文件就得換回普通賬戶(hù)加存密碼的方式。4.3 起始于、失敗重試和「上次運(yùn)行結(jié)果」圖形界面里「操作」那一頁(yè)有個(gè)「起始于可選」字段很多人跳過(guò)不填。這個(gè)字段設(shè)的是任務(wù)啟動(dòng)時(shí)進(jìn)程的當(dāng)前目錄不填的話(huà)就是C:\Windows\System32。批處理里如果已經(jīng)寫(xiě)了cd /d這個(gè)字段填不填無(wú)所謂如果批處理依賴(lài)相對(duì)路徑這里必須填否則就是前面說(shuō)的那種「手工跑正常、計(jì)劃跑找不到文件」。schtasks命令行沒(méi)有直接對(duì)應(yīng)「起始于」的參數(shù)要設(shè)的話(huà)得導(dǎo)出 XML 改WorkingDirectory節(jié)點(diǎn)或者把/tr寫(xiě)成cmd /c cd /d C:\datatran 1.bat。后者更省事也不依賴(lài) XML 的格式版本?!溉绻蝿?wù)失敗按以下頻率重新啟動(dòng)」這個(gè)設(shè)置在 FTP 場(chǎng)景里很有用。對(duì)端偶爾網(wǎng)絡(luò)抖動(dòng)、服務(wù)端連接數(shù)滿(mǎn)了、DNS 解析慢都會(huì)導(dǎo)致一次連接超時(shí)重試兩三次基本能覆蓋掉。頻率設(shè) 5 分鐘、重試 3 次是個(gè)比較穩(wěn)的組合間隔太短會(huì)在對(duì)端還沒(méi)釋放連接的時(shí)候又頂上去。跑完之后看「上次運(yùn)行結(jié)果」那一列幾個(gè)常見(jiàn)碼記一下0x0是成功0x1是一般性錯(cuò)誤通常是腳本里的命令返回了非零0x2是找不到文件路徑寫(xiě)錯(cuò)了0x41301是任務(wù)正在運(yùn)行不是錯(cuò)誤是還沒(méi)跑完0x8007052E是登錄失敗密碼失效??吹?x0也別急著放心前面說(shuō)過(guò)FTP 連不上但腳本正常退出退出碼也可能是 0真正的判斷要看日志內(nèi)容。5. 五次真實(shí)環(huán)境故障從卡在 y/n 到「只在登錄時(shí)運(yùn)行」下面這幾條都是在這類(lèi)腳本上真實(shí)遇到過(guò)的按「現(xiàn)象 → 原因 → 解決」寫(xiě)遇到類(lèi)似癥狀可以直接對(duì)照。5.1 現(xiàn)象任務(wù)狀態(tài)一直「正在運(yùn)行」日志停在一串mget xxx?原因腳本里漏了promptmget匹配到多個(gè)文件時(shí)逐個(gè)等確認(rèn)無(wú)人值守環(huán)境下沒(méi)人敲 y進(jìn)程一直掛著直到超時(shí)退出。原料里的腳本是有prompt的但很多人抄的時(shí)候只抄了open、user、cd、mget、bye幾行把prompt和bin當(dāng)成可選行略掉了。解決腳本里mget之前必須有一行prompt命令行再加-i做兜底。已經(jīng)卡住的任務(wù)要在任務(wù)計(jì)劃里手動(dòng)「結(jié)束」掉否則下一次觸發(fā)會(huì)因?yàn)椤溉蝿?wù)已在運(yùn)行」被跳過(guò)看起來(lái)像是徹底不跑了。5.2 現(xiàn)象手工雙擊 bat 一切正常掛進(jìn)計(jì)劃就報(bào)「找不到文件」原因兩種可能。一是批處理里沒(méi)有cd /d任務(wù)計(jì)劃的當(dāng)前目錄是System32相對(duì)路徑全部失效。二是任務(wù)計(jì)劃的「起始于」為空而 bat 里引用腳本時(shí)用了相對(duì)路徑比如-s:1.ftp而不是-s:C:\scripts\1.ftp。解決bat 第一行寫(xiě)cd /d C:\datatran腳本路徑一律用絕對(duì)路徑/tr外面套cmd /c。三件事一起做這類(lèi)問(wèn)題基本不再出現(xiàn)。5.3 現(xiàn)象壓縮包傳過(guò)去解壓報(bào) CRC 錯(cuò)誤但文件大小差不多原因用了 ASCII 模式傳二進(jìn)制文件。原料里那句「Windows 之間傳就去掉 bin」被照搬到所有場(chǎng)景結(jié)果傳的是 zip、dmp、exe。ASCII 模式把文件里恰好出現(xiàn)的0x0D0A當(dāng)成行尾改寫(xiě)大小差別不大內(nèi)容已經(jīng)錯(cuò)了。解決二進(jìn)制文件一律bin文本文件才用ascii按文件類(lèi)型拆成不同腳本。已經(jīng)傳壞的文件沒(méi)法修只能重傳如果對(duì)端有歷史備份比對(duì)一下大小和校驗(yàn)和就能確認(rèn)是哪一批壞的。5.4 現(xiàn)象白天測(cè)試都正常下班之后任務(wù)就再也不跑了原因任務(wù)計(jì)劃用了默認(rèn)的「只在用戶(hù)登錄時(shí)運(yùn)行」。測(cè)試的時(shí)候人坐在機(jī)器前會(huì)話(huà)一直在下班注銷(xiāo)或者機(jī)器重啟之后沒(méi)人登錄任務(wù)就沒(méi)有可用的登錄令牌觸發(fā)時(shí)直接被跳過(guò)。圖形界面里這個(gè)選項(xiàng)藏在「常規(guī)」頁(yè)頂部向?qū)Ю锔静粫?huì)提示。解決改成「不管用戶(hù)是否登錄都運(yùn)行」并輸入密碼或者把運(yùn)行賬戶(hù)換成SYSTEM。改完之后做一次驗(yàn)證注銷(xiāo)當(dāng)前用戶(hù)等一個(gè)觸發(fā)周期再登錄進(jìn)來(lái)看「上次運(yùn)行結(jié)果」和日志文件有沒(méi)有新內(nèi)容。不注銷(xiāo)就驗(yàn)證的等于沒(méi)驗(yàn)證。5.5 現(xiàn)象賬號(hào)密碼都對(duì)就是登錄失敗日志里user那一行被截?cái)嗔嗽騀TP 腳本按空格分詞密碼里帶空格、#、;這類(lèi)字符時(shí)user 用戶(hù)名 密碼這一行會(huì)被拆成多段客戶(hù)端把第二段當(dāng)成密碼后面的當(dāng)成未知命令。原料示例里密碼是123456看著沒(méi)問(wèn)題換成真實(shí)環(huán)境的復(fù)雜密碼就中招。解決最直接的辦法是把密碼改成不含空格和特殊符號(hào)的字符串。改不了的話(huà)就別把密碼寫(xiě)死在腳本里改成每次運(yùn)行前用echo生成一個(gè)臨時(shí)腳本跑完刪掉——雖然多了幾步但至少不會(huì)被分詞坑到。另外把明文密碼放在.ftp文件里本身就有風(fēng)險(xiǎn)文件權(quán)限要收緊到只有運(yùn)行賬戶(hù)可讀。6. 從「能跑」到「跑壞了知道」退出碼判斷、日志計(jì)數(shù)與過(guò)期清理腳本能定時(shí)跑起來(lái)只是及格線(xiàn)真正省心的是它出問(wèn)題的時(shí)候你能第一時(shí)間知道。ftp.exe的退出碼信息量很有限連接失敗返回 2命令執(zhí)行中出錯(cuò)返回 1 或 0光看退出碼判斷不了「文件到底傳沒(méi)傳全」??尚械霓k法是回到日志里數(shù)226 Transfer complete出現(xiàn)的次數(shù)和預(yù)期文件數(shù)比。下面這段是可以直接用的收尾邏輯echo off setlocal set LOGDIRC:\logs if not exist %LOGDIR% md %LOGDIR% set LOG%LOGDIR%\ftp_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log cd /d C:\datatran ftp -n -v -s:C:\scripts\1.ftp %LOG% 21 rem 統(tǒng)計(jì)成功傳輸次數(shù)和預(yù)期文件數(shù)比對(duì) set EXPECT12 for /f %%c in (findstr /c:226 Transfer complete %LOG% ^| find /c /v ) do set ACTUAL%%c if not %ACTUAL%%EXPECT% ( echo [%DATE% %TIME%] 傳輸數(shù)不符 預(yù)期%EXPECT% 實(shí)際%ACTUAL% %LOGDIR%\alert.log exit /b 1 ) rem 清理 7 天前的日志避免目錄無(wú)限膨脹 forfiles /p %LOGDIR% /m *.log /d -7 /c cmd /c del path nul 21 exit /b 0關(guān)鍵在for /f那一行findstr篩出所有含226 Transfer complete的行管道交給find /c /v 計(jì)數(shù)。find /c單獨(dú)用會(huì)要求輸入文件名配上/v 表示「統(tǒng)計(jì)所有不含空串的行」也就是統(tǒng)計(jì)全部行這是批處理里數(shù)行數(shù)的老技巧。EXPECT要按實(shí)際情況改文件數(shù)每天變的話(huà)可以改成和遠(yuǎn)端目錄列表比對(duì)或者干脆只判斷「實(shí)際數(shù)是否大于 0」。forfiles /d -7按修改時(shí)間篩出 7 天前的文件/c cmd /c del path逐個(gè)刪掉日志目錄就不會(huì)越堆越大。還有一個(gè)容易被忽略的點(diǎn)%ERRORLEVEL%在setlocal作用域里是即時(shí)值但在if塊里引用時(shí)如果是延遲展開(kāi)環(huán)境要用!ERRORLEVEL!。上面的寫(xiě)法把ftp和判斷拆在兩條獨(dú)立語(yǔ)句里避開(kāi)了這個(gè)坑代價(jià)是丟掉了ftp的原始退出碼。要兩個(gè)都保留的話(huà)在ftp那行之后立刻set RC%ERRORLEVEL%再用%RC%判斷。從那以后我每次掛無(wú)人值守任務(wù)都強(qiáng)制走一遍「手工跑 → 看日志 → 注銷(xiāo)會(huì)話(huà)再跑一次」這三步一步都不省。省下的那十分鐘往往要用一個(gè)通宵來(lái)找回來(lái)。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取