有天早上開會,主管要一份「昨天備份到底存了哪些檔案」。我手上有備份腳本吐出的日誌,三百一十二行,每行一個完整路徑,形如 D:\backup\2024-03-12\site\index.html。貼到共用文件裡滿螢幕都是目錄前綴,誰也看不出重點。我最初的打算是在記事本裡逐行刪前半段,刪了不到五十行就放棄了——不是刪不動,是每行要刪的字元數不一樣。
斜線方向不是風格問題,是兩套檔案系統
Windows 用反斜線 \ 分隔目錄,Unix 和 Linux 用正斜線 /。同一批資料裡兩種都可能遇到:日誌是 Windows 上的服務寫的,附件路徑卻是從物件儲存的介面來的。混在一起手工刪就有風險——只按正斜線找「最後一個分隔符」,遇到 C:\Users\name\a.txt 會一個都找不到,把整行當檔名交出去。
我用 檔名提取 的原因很簡單:它按行批次處理,反斜線和正斜線兩種分隔都能辨識,三百多行一次貼進去就出結果。省時間的不是少按鍵,而是不用逐行判斷這行屬於哪種路徑。
還有一點值得記:Linux 裡反斜線是合法的檔名字元,/tmp/a\b.txt 的反斜線屬於名字本身,不是分隔符。我第一次做批次擷取時把 Windows 習慣套到一份 Linux 日誌上,幾個怪名字的檔案全被切錯。來源混雜的資料,我會先按來源分堆再分別處理。
磁碟機代號、UNC 和結尾斜線這三處最容易翻車
第一處是磁碟機代號。C:\Users\name\a.txt 裡的冒號出現在 C: 上,它不是分隔符,但手工按冒號或者按斜線切分的人常在這裡多切一刀,得到 \Users\name\a.txt 之類的殘段。
第二處是 UNC 路徑,網路共用的寫法:\\server\share\a.txt。開頭兩個連續反斜線不是分隔符重複,它是「這是共用路徑」的標記,後面伺服器名和共用名各佔一段。我手工刪的時候把 \\nas01\files\report.pdf 的第一個反斜線當成跳脫刪掉了,那行貼進批次腳本後再也不是合法路徑。
第三處是結尾帶斜線的路徑,比如 /home/deploy/app/。這是一個目錄,斜線後面是空的。按「取最後一段」的規則,結果是空字串而不是 app。我現在的檢查動作是把結果行數和輸入行數對一下,看有沒有空行冒出來——有空行就說明原始資料裡有目錄形態的路徑,那一類要麼單獨處理,要麼明確「目錄名不算檔名」。
要不要副檔名,先問聽眾
這是擷取動作裡第二個要決定的事。給維運看,帶副檔名才有用,access.log 和 access 是兩個東西;給主管做彙報清單,多數時候只要名字,一整排 .pdf、.zip 反而干擾閱讀。工具這邊可以選是否包含副檔名,所以我一般兩版都跑,自己留帶副檔名的那份,交出去用不帶的。
去掉副檔名靠的是最後一個點。例外是 pack.tar.gz:按最後一個點切得到 pack.tar 和 gz。我那份備份清單裡就有這種封存檔,不帶副檔名那版留著 .tar,一眼看過去像命名不規則。要按 .tar.gz、.tar.bz2 這種多段副檔名正確分離,得用 檔名後綴拆分,別把普通「去後綴」的結果當成錯。
反過來還有沒副檔名的檔案。Linux 下 /opt/bin/nginx、/etc/passwd 都沒有點,這種行開著「不含副檔名」時我就特別小心,規則不明確會削掉真實名字的一部分,我會把它們單獨挑出來對一遍。
去重改變的是筆數,不是格式
三百一十二行裡,勾上去重後只剩兩百零五個名字。差出來的一百零七個不是錯誤,是同名檔案:index.html 在七個站台目錄裡各有一份,app.log 因為一天滾動多次出現了十幾行。擷取只留下最後一個分隔符之後的部分,前面的目錄資訊是區分它們的唯一線索,去重之後歸屬就查不回去了。
所以我把「要不要去重」變成一句要回答的問題:要的是檔案種類數,還是筆數。種類數就去重;筆數就不去重,因為備份是不是每個都做了,看的是三百一十二這個數。兩者都要,我在去重前先留一份帶路徑的原始結果。
| 輸入路徑(原樣) | 擷取到的檔名 | 不含副檔名 | 要點 |
|---|---|---|---|
C:\Users\name\Documents\a.txt | a.txt | a | 磁碟機代號裡的冒號不是分隔符 |
\\server\share\backup\report.pdf | report.pdf | report | 開頭兩個反斜線是共用標記 |
/var/log/nginx/access.log | access.log | access | Unix 下反斜線可屬於檔名 |
D:\data\archive\pack.tar.gz | pack.tar.gz | pack.tar | 按最後一個點分,多段副檔名要另處理 |
/home/deploy/app/ | 空(最後是斜線) | 空 | 這是目錄,名字在倒數第二段 |
/opt/bin/nginx | nginx | nginx | 本身無副檔名,別多削一刀 |
file:///data/report.pdf | report.pdf | report | 協定前綴會被當成目錄段 |
最後一行那種 file:/// 前綴是我另一份資料裡遇到的,會被當成一段目錄處理,檔名本身取對了;但如果我要的是去掉前綴保留相對路徑,那就不是擷取的活——我會先用 文字行操作 刪掉每行左側固定數量的字元再擷取,順序不能反。前綴長度不固定時這種按位置的做法會失效,得改成取代。
幾個我當時踩過的疑問
Q:路徑裡帶空格或者中文,會不會切斷?
不會。分隔符只有斜線那兩種,空格和中文都屬於名字的一部分,C:\我的檔案\年報 2024.docx 擷取出來是完整的 年報 2024.docx。真要出問題的是貼進腳本時沒給路徑加引號,那是引用語法的問題。
Q:日誌裡一行有幾個路徑怎麼辦?
批次擷取按行來。我遇到一行裡兩個路徑的日誌,先拆成一行一個再提,不拆的話取到的分隔符位置來自最後一個路徑,前面那一整個會被削掉。
Q:目錄名和檔名混在一起,能只提檔案嗎?
我不指望自動區分——結尾是斜線的那一行是我的判斷依據,我會把那些行單獨摘出來當目錄處理,剩下的按檔案提。混著交出去最容易出空結果。
Q:擷取後順序會變嗎?
輸出與輸入按行一一對應,所以我能拿兩邊行數直接比。核對時我只看兩個數:行數是否相等、結果裡空行數是否為零。
Q:幾百行會不會一次處理不過來?
我那批三百一十二行是一次貼進去的,沒分批,核對很直觀——行數相等就通過。真要上萬行我更擔心的不是處理而是核對,那種量級我會先按目錄分組、一段一段提,免得對不上時無從回溯。
回到那份備份清單。最後交出去的是兩百零五個不帶副檔名的名字,我在旁邊標了一行「原始記錄 312 筆,同名檔案已合併」,主管要的「存了哪些」和維運要的「每個都存了」這兩個口徑就分開了。要不要去重、要不要副檔名、結尾有斜線的行怎麼處理——這三個決定比「用哪個工具」更影響結果。