标签
PHP兩陣列相加少了一筆:+與array_merge怎麼選
把兩批文章標題放進陣列,用$left + $right合起來,結果右邊第一筆不見了。PHP陣列的加號是按鍵名做聯集,不是把所有值接到後面。兩邊有相同鍵名時,左邊的值會留下,右邊對應的值會被忽略。 兩個列表都從0開始,第一個鍵就撞上了 以下每段都是獨立PHP範例,可各自存成.php檔執行。註解列出預期輸出,資料只是示例字串。 <?php $left = ['header', 'footer']; $right = ['sidebar']; echo json_encode($left + $right), PHP_EOL; // ["header","footer"] echo json_encode(array_merge($left, $right)), PHP_EOL; // ["header","footer","sidebar"] left的鍵是0、1;right的鍵也是0。因此,sidebar與header搶的是同一個鍵,加號保留左邊的header,並不是按文字內容去除重複。若只是要把兩批列表依序接起來,array_merge會保留三個值,並把整數鍵從0重新編號。 字串鍵相同時,兩種寫法的優先順序相反 <?php $left = ['color' => 'blue', 'rows' => 12]; $right = ['color' => 'red', 'cache' => true]; echo json_encode($left + $right), PHP_EOL; // {"color":"blue","rows":12,"cache":true} echo json_encode(array_merge($left, $right)), PHP_EOL; // {"color":"red","rows":12,"cache":true} 加號保留左邊color的blue;array_merge則由後面的red覆蓋前面的值。右邊獨有的cache,在兩種結果裡都會出現。合併網站設定時,先決定同鍵衝突要由誰優先,再選寫法;交換左右位置也會改變結果。 這個例子只處理一層鍵值。巢狀設定裡若還有陣列,不能直接把它當成逐層遞迴合併。 數字是文章ID時,不要順手重編鍵名 <?php $left = [101 => 'Article A']; $right = [202 => 'Article B']; echo json_encode($left + $right), PHP_EOL; //...
Git log -S漏掉改動?用-G找增刪行
想找「哪次提交把timeout從10改成30」,明明看過那筆修改,git log -S 'timeout'卻沒列出來。原因可能不是紀錄消失,而是搜尋條件不同:-S看指定字串在檔案裡的出現次數有沒有變;-G則檢查差異裡新增或刪除的行是否符合正規表示式。 值改了,字串出現次數卻沒變 假設config.txt原本只有一行timeout=10,下一次提交改成timeout=30。兩個版本都只出現一次timeout;因此,以timeout作為-S條件,不會因為數值變動而選出這筆提交。 git log --oneline -S 'timeout' -- config.txt git log --oneline -G 'timeout' -- config.txt 第二條會檢查patch裡的增刪行。因為刪掉的timeout=10、新增的timeout=30都符合timeout,所以-G能找到這次改值。第一條仍可能列出最早新增timeout的提交,不能把「有搜尋結果」誤認為已找到這次修改。 用一個練習庫,看清楚兩組結果 在獨立練習資料夾建立Git儲存庫,先提交config.txt,內容為timeout=10,提交訊息用base;再改成timeout=30,提交訊息用raise timeout;最後補上第二行retry=2,提交訊息用add retry。每次都先git add config.txt再提交。完成後查詢: git log --format=%s -S 'timeout' -- config.txt # base git log --format=%s -G 'timeout' -- config.txt # raise timeout # base git log --format=%s -S 'timeout=10' -- config.txt # raise timeout # base 這裡的註解是預期顯示的提交訊息,不是另一條命令。-S 'timeout=10'能找到raise timeout,是因為完整字串timeout=10從一次變成零次;搜尋timeout時,它仍各出現一次。最後新增retry那筆不符合這些條件,所以不會列出。 找到提交後,再看實際改了哪一行 git log -p -G '^timeout=' -- config.txt # 若已找到要核對的提交,將COMMIT換成它的雜湊 git show COMMIT -- config.txt ^timeout=只比對行首為timeout=的增刪行,能縮小搜尋範圍;-p會把差異一併顯示。選中提交後,核對刪除行、新增行、檔案路徑及上下文,再判斷它是否就是造成問題的修改。這些查詢不會把工作目錄切回舊版。 搜不到時,先檢查條件與範圍...
Bash前段指令失敗卻回傳0:用pipefail檢查管線
一段檢查或處理資料的指令,用豎線接到下一段後,前段明明失敗,整條管線卻回傳0。若你用這個結果決定是否繼續備份、部署或匯入,可能漏掉前面的錯誤。Bash一般以管線最後一段的退出狀態作為整體結果;啟用pipefail後,才會把前段的非零狀態納入判斷。 先重現:前段失敗,最後一段仍成功 以下用false刻意回傳1、true回傳0,方便觀察規則,不會處理檔案。請存成.sh檔並用bash 檔名.sh執行;範例使用Bash,不能假設所有以sh執行的環境都有相同選項。 #!/usr/bin/env bash set +o pipefail false | true rc=$? printf 'pipeline status=%s\n' "$rc" # pipeline status=0 set -o pipefail false | true rc=$? printf 'pipeline status=%s\n' "$rc" # pipeline status=1 第一組關閉pipefail,管線狀態取自最後的true,所以是0。第二組啟用後,前段false的1成為整條管線的狀態。若所有段落都回傳0,啟用pipefail後的整體結果仍是0。 rc=$?要緊接管線。若先執行echo或另一條指令,$?就會變成那條指令的狀態;上面的printf是在保存rc之後才顯示結果。 多段都失敗時,取最右側的非零狀態 #!/usr/bin/env bash set -o pipefail (exit 7) | (exit 3) | true rc=$? printf 'pipeline status=%s\n' "$rc" # pipeline status=3 結果是3,不是7,也不是最後一段true的0。pipefail取管線位置最右側、且退出狀態非零的那一段;不是取最大的數字,也不是取最後完成執行的程序。括號裡的exit只結束該子shell,不會直接結束外層腳本。 取得失敗狀態後,還要決定下一步 pipefail本身不等於「出錯立即停止」。前面的第二組範例中,管線回傳1後,printf仍會執行。若這個步驟失敗就必須停止,請明確寫出分支,並保留原來的狀態。 #!/usr/bin/env bash set -o pipefail if false | true; then printf '檢查完成,繼續下一步\n' else rc=$? printf '管線未通過,status=%s\n' "$rc" >&2 exit "$rc"...
MacCMS 還在用 PHP 7.2?PHP2Shell 緩解、修補與升級路線
MacCMS 網站還在用 PHP 7.2,遇到 PHP2Shell 時,不能只在面板按「更新至 7.2 最新版」便結案。PHP 7.2 已於 2020 年 11 月 30 日結束上游支援,最後一版 7.2.34 早於這次漏洞。先確認自己的 7.2 是誰提供的,再決定使用供應商回移補丁,或把 MacCMS 搬到已修補且仍受支援的 PHP 環境。 短期可在正確的 PHP 設定中關閉 session.upload_progress.enabled,減少部分攻擊路徑;這不是完整修復。若沒有可核實的修補,應先限制站點的外部存取,利用維護窗口完成副本測試,不要把「舊模板只能用 7.2」當成可以一直公開運行的理由。 先分清:Ubuntu 的 PHP 7.2,還是寶塔的 PHP 7.2? 登入 SSH 後先盤點,不要先改設定: cat /etc/os-release command -v php php -v ps -eo pid,ppid,args | grep '[p]hp-fpm' systemctl list-units --type=service --all | grep -E 'php.*fpm' 如果系統有 APT,再查是否真的安裝了發行版的 7.2 套件: dpkg-query -W php7.2-common php7.2-fpm apt-cache policy php7.2-common php7.2-fpm 找不到這些套件,不等於網站沒有 PHP。寶塔或其他面板可能維護自己的安裝目錄,網站也可能在容器內。請把 MacCMS 網域的 FastCGI socket/連接埠,對到實際 FPM master 與...
PHP2Shell SSH 處理教學:停用上傳進度、升級 PHP 與重啟驗證
能登入 SSH 的站長,處理 PHP2Shell 時先做三件事:找出網站真正使用的 PHP、在正確位置套用臨時緩解、更新 PHP 並重啟對應工作程序。下面以 Ubuntu 24.04、APT 安裝的 PHP 8.3-FPM 為完整操作例子,另說明寶塔及 Docker 的分流;路徑與服務名稱要跟自己的環境核對,不能整篇不看就貼上。 PHP 官方已在 2026 年 10 月 21 日修補 CVE-2026-109618 與 CVE-2026-109619。上游修補版是 8.2.35、8.3.36、8.4.27、8.5.12。發行版可能回移修補,請比對完整套件版本;例如 Ubuntu 24.04 的公告修補版為 8.3.6-0ubuntu0.24.04.13。 登入 SSH 後,先看 PHP 是誰安裝的 先執行以下唯讀命令。它們用來找線索,尚未改動設定或更新套件: cat /etc/os-release command -v php php -v php --ini ps -eo pid,ppid,args | grep '[p]hp-fpm' systemctl list-units --type=service --all | grep -E 'php.*fpm|apache2|httpd' php -v 和 php --ini 顯示的是命令列 PHP,未必是網站的 PHP。還要對照網站的 Nginx/Apache 虛擬主機設定、FastCGI socket,以及對應 FPM master 的啟動路徑。寶塔可能使用自己的 PHP 目錄;容器內的 PHP 也不會隨宿主機的 APT...
PHP2Shell 高危漏洞已修補:WordPress 站長該確認什麼?
PHP 官方於 2026 年 10 月 21 日發布緊急安全更新,修補俗稱 PHP2Shell 的兩個高危漏洞。使用 WordPress 的站長,現在要向主機商確認的是:網站實際使用的 PHP 是否已套用修補,以及更新後的工作程序是否已重新啟動。只在 WordPress 後台按下核心、主題或插件更新,無法完成這次 PHP 執行環境的修補。 PHP2Shell 影響哪些網站? 這次涉及 CVE-2026-109618 與 CVE-2026-109619,官方預告列出的 CVSS v4.0 評分均為 9.2。問題出在 PHP 核心處理請求與記憶體的方式,可能讓未登入的遠端攻擊者執行程式碼。 PHP 官方把所有提供網頁請求的 PHP 安裝列為風險範圍,包括 PHP-FPM、Apache 的 mod_php、php-cgi、FrankenPHP 與 LiteSpeed。當中 mod_php 的風險被列為最高,已有在應用程式碼執行前觸發攻擊的示範。因此,網站有登入頁、關閉會員註冊,或沒有讓訪客上傳檔案,都不能直接當成安全判定。 兩份上游公告分別描述記憶體耗盡時的狀態損壞,以及前一次請求留下的全域狀態引用了已釋放的記憶體。這也解釋了為何處理範圍會超出單一 WordPress 插件:維護者需要檢查主機上的 PHP 本身。 先對照分支,再確認修補來源 PHP 官方已發布下列修補版本;若採用上游版本,請更新至所屬分支的這個版本或更新版本。 PHP 8.2:8.2.35。 PHP 8.3:8.3.36。 PHP 8.4:8.4.27。 PHP 8.5:8.5.12。 由 Linux 發行版或長期支援供應商維護的套件,可能採用回移補丁:保留原本的基礎版本號,把安全修正加進套件。例如 Ubuntu 官方列出,24.04 的修補套件為 8.3.6-0ubuntu0.24.04.13,22.04 則為 8.1.2-1ubuntu2.28。不能因為畫面仍顯示 8.3.6,就斷定它一定沒有修補;也不能只聽到「有自動更新」就認定完成。請核對完整套件版本和供應商公告。 PHP 8.1 及更舊版本已不再獲得 PHP 專案的正常更新。官方這次提供了 7.4、8.0、8.1 的最小原始碼回移修補,供特定維護情境使用,並不代表這些分支恢復支援。若主機商無法提供相應修補,應安排遷移至仍受支援的 PHP 版本,先確認網站、主題與插件的相容性。 WordPress 站長可以怎樣向主機商確認? 先登入自己的 WordPress...
JS填0卻變預設值:分清雙豎線與空值合併
設定值明明是0,畫面卻套用了預設的10;開關明明是false,結果又變成開啟。先找程式裡是否用了value || 預設值。雙豎線會把0、false和空字串等falsy值一起當成切換到右側的條件。若只有null與undefined才代表缺值,應改用??。 0有值,雙豎線仍會換掉它 這個例子假設0是允許的設定,10才是缺值時的預設。可直接在瀏覽器Console或Node.js執行。 const configuredCount = 0 console.log(configuredCount || 10) // 10 console.log(configuredCount ?? 10) // 0 ||不是只檢查變數有沒有資料。它先判斷左側是否為truthy;若不是,就回傳右側的值。??的條件較窄,只在左側為null或undefined時採用右側;0會原樣保留。 關閉與空白,也可能是刻意的選擇 const enabled = false const customLabel = '' console.log(enabled || true) // true console.log(enabled ?? true) // false console.log(customLabel || '未填') // 未填 console.log(customLabel ?? '未填') // 空字串 若false表示關閉某項功能,使用|| true會把它改成true。若空字串表示「不要顯示標籤」,||也會補回文字。先決定這些值在你的功能裡是否合法,再選運算子;不是所有預設值都應一律改成??。 例如名稱欄位若不允許空白,保留空字串就不符合需求。那是欄位驗證與資料整理的責任,不能只靠??替你判斷。 真正缺值時,才採用預設 const settings = { count: 0, label: null } console.log(settings.count ?? 10) // 0 console.log(settings.label ?? '未填') // 未填 console.log(settings.delay ?? 5) // 5 這裡settings已經是物件;沒有delay屬性,讀取結果為undefined,因此採用5。label明確設為null,也會採用預設。這不表示??能讓不存在的變數或任何屬性存取都安全,它處理的是左側已求值後的結果。 保留值,不等於值已經合法 ??不會把字串「0」轉成數字,也不會把NaN變成預設值。若資料來自表單或API,仍要依資料格式確認型別、範圍與缺值規則,再把可用資料帶進畫面。...
PHP foreach跑完資料卻變了:引用變數用完記得unset
第一輪foreach改完價格,資料明明是11、21、31;接著只是再走訪一次,最後一項卻變成21。若前一輪寫了&$price,先查迴圈結束後有沒有unset($price)。引用不會隨著右大括號自動解除,後面再用同一個變數,就可能改到陣列尾項。 第二輪看似只讀,為何改了尾項 把下面程式存成PHP檔執行。第一輪透過引用替每個值加1,第二輪沒有寫任何更新陣列的程式,卻仍會改變結果。 <?php $prices = [10, 20, 30]; foreach ($prices as &$price) { $price += 1; } echo json_encode($prices), PHP_EOL; // [11,21,31] foreach ($prices as $price) { // 這裡沒有主動修改陣列 } echo json_encode($prices), PHP_EOL; // [11,21,21] 第一輪結束時,$price仍連著$prices最後一項。第二輪雖然沒有&,每次把元素值指派給$price,仍會透過殘留引用寫進尾項;輪到最後一項時,它已被前面的21覆蓋。問題不是json_encode,也不是陣列排序。 引用用完,立即解除連結 在引用迴圈結束後加上unset($price),再開始下一輪。用完整程式重新執行,避免前一次實驗留下的引用干擾結果。 <?php $prices = [10, 20, 30]; foreach ($prices as &$price) { $price += 1; } unset($price); foreach ($prices as $price) { // 後續一般走訪 } echo json_encode($prices), PHP_EOL; // [11,21,31] 這裡unset的是引用變數$price,解除它與陣列元素的連結,不是刪除$prices的最後一項。不要誤寫成unset($prices[2]),那會移除元素。第一輪已完成的加1結果,也不會因為解除引用而還原。 不需要引用,就明確按key回寫 若只是更新每一項,可以改用key指定位置,把引用的生命週期問題拿掉。 <?php $prices = [10, 20, 30]; foreach ($prices as...
HTML寫了checked=”false”還是勾選:分清屬性與目前狀態
模板輸出checked="false",核取方塊卻還是打勾?在原生HTML裡,checked是布林屬性;寫進標籤就表示預設勾選,不會把字串false當成關閉指令。若要預設不勾選,應該省略整個checked屬性。 false不是checked的合法關閉值 先把下面兩個欄位放進一個空白頁,比較初始畫面。第一個是常見的錯誤輸出,第二個才是預設不勾選的寫法。 <!-- 錯誤示範:仍會預設勾選 --> <label> <input type="checkbox" checked="false"> 接收更新通知 </label> <!-- 預設不勾選:不輸出checked --> <label> <input type="checkbox"> 接收更新通知 </label> 布林屬性可只寫名稱,例如checked;也可寫checked=""或checked="checked"。字串true和false都不是合法取值。瀏覽器仍會因為屬性存在而把第一個方塊設為預設勾選,但不要把這種容錯行為當成正確格式。 模板應決定是否輸出整個屬性 檢查模板最後送到瀏覽器的HTML。當設定為關閉時,應得到沒有checked的input,而不是checked="false"或checked="0"。若資料來自資料庫或API,先依資料格式把它轉成真正的布林值,再決定是否輸出checked;別直接把字串塞進屬性。 這裡說的是原生HTML。框架的屬性綁定有自己的語法與轉換規則,不能把原始碼中的綁定表達式直接當成最後的HTML。先看瀏覽器裡實際產生的元素,再查該框架的綁定文件。 頁面載入後,讀checked目前狀態 如果要在JavaScript裡取消勾選,用元素的checked屬性,給它布林值false。以下HTML與後面的程式碼可接在一起測試;script放在表單後面,確保先取得元素。 <form id="demo"> <label> <input id="notice" type="checkbox" checked> 接收更新通知 </label> <button type="reset">重設表單</button> </form> const notice = document.querySelector('#notice') notice.checked = false console.log(notice.checked) // false:目前狀態 console.log(notice.defaultChecked) // true:預設狀態 console.log(notice.hasAttribute('checked')) // true 使用者手動取消勾選時,HTML上的checked也不一定消失。判斷「現在有沒有勾選」應讀notice.checked,不要用getAttribute('checked')或屬性是否存在來代替。 重設後又勾選,先查預設狀態 在上面的表單按「重設表單」,方塊會回到預設勾選。notice.checked=false只改了當下狀態,沒有把HTML的預設值一起改掉。若需求是連重設後也維持預設不勾選,就要另外調整預設狀態;不要只在畫面上取消一次便以為完成。 還要分清checked與value。value是欄位勾選並提交時使用的值,不是打勾開關。一般表單中,未勾選的checkbox不會提交該欄位的名稱與值;後端若需要明確的關閉值,須按自己的資料格式處理,不能期待checked="false"替你送出false。 參考資料 MDN:HTML布林屬性的合法寫法 MDN:checked與defaultChecked MDN:checkbox預設狀態及提交值
CSS寬度100%還是超出容器:把padding算進box-sizing
卡片已設width:100%,加上內距後,右邊卻超出容器。先看這張卡片的box-sizing:若寬度只算內容區,左右padding與border還會再加上去。把它改成border-box,才會把這些空間算進指定寬度。 先用一張卡片看出差別 把下面HTML與CSS放進空白頁。外層寬300px,卡片寬100%,左右各有24px內距和2px邊框。這裡刻意指定content-box,避免你的主題原本已設border-box,讓問題無法重現。 <div class="demo-frame"> <div class="demo-card">卡片內容</div> </div> .demo-frame { width: 300px; max-width: 100%; } .demo-card { box-sizing: content-box; width: 100%; padding: 24px; border: 2px solid #126477; } 在外層確實有300px可用、邊框按2px計算時,卡片的邊框外緣總寬是300+24×2+2×2,也就是352px。100%只填滿內容區,並沒有替左右多出的52px預留位置。瀏覽器縮放可能讓邊框換算後出現小數,實際尺寸請看開發者工具的盒模型。 只改卡片的box-sizing .demo-card { box-sizing: border-box; width: 100%; padding: 24px; border: 2px solid #126477; } 同樣300px的可用寬度,現在padding與border都放在這300px裡,內容區則縮小。以邊框2px計,內容區剩248px。這個案例沒有margin;border-box也不會把margin包進width,若另加左右外距,仍須另外計算。 在原網站裡確認改到哪一層 打開瀏覽器開發者工具,選到超寬卡片本身,查看Computed裡的box-sizing、width、padding和border。先只切換這張卡片的box-sizing,比較左右邊界是否回到容器內,再把確定有效的規則寫回對應元件。 不要只看CSS檔裡有沒有border-box:選擇器可能沒有套中,也可能被後面的規則覆蓋。這個屬性預設不繼承,父層設定不代表所有後代都有同樣值;部分表單元件的瀏覽器預設也不同。修改既有主題時,先限定目標卡片,別為一個區塊直接重設全站。 還在超寬,就查別的來源 border-box處理的是指定寬度如何容納padding與border。固定寬圖片、不能換行的長字串、額外margin,或Flex子項的最小寬度,仍可能造成溢出。先找出真正超出邊界的元素,不要用整頁overflow:hidden把問題和內容一起藏掉。 最後把容器縮窄,檢查卡片內容有沒有被壓得難以閱讀。內容區最小只能到0;當padding與border本身已比可用寬度大,border-box也不能保證塞得下。此時要調小內距、調整元件排列,或重新分配空間。 參考資料 MDN:box-sizing與盒模型計算 MDN:width及百分比寬度
JS明明return成功卻拿到別的值:檢查finally裡的return
函式的try裡已經寫了return 'ready',呼叫端卻收到「done」?先往下看finally。在一般JavaScript函式中,try準備回傳後仍會執行finally;若finally又執行return,它會覆蓋原本的回傳結果。 兩個return,最後收到哪一個? 把下面整段貼到瀏覽器Console,或存成檔案用Node.js執行。這個例子沒有網路請求,只有函式的回傳順序。 function readStatus() { try { return 'ready' } finally { return 'done' } } console.log(readStatus()) // done 這不是try沒有執行,也不是呼叫端讀到舊值。程式準備把ready交回去時,先進入finally;那裡的第二個return改了最後結果。所以只在try的回傳行設中斷點,可能看不出問題。 不帶值的return也會覆蓋 有時候finally裡沒有寫替代值,只是加了一個看似無害的提早離開。它仍然會影響結果。 function readStatusWithoutValue() { try { return 'ready' } finally { return } } console.log(readStatusWithoutValue()) // undefined 呼叫端得到undefined,並不代表try沒有找到資料;也可能是finally中的空return把原值蓋掉了。不要用「finally沒有回傳新資料」判斷它不影響結果。 原本丟出的錯誤,也可能被藏起來 這個問題不只發生在成功分支。try已經丟出錯誤,但finally回傳一個普通值時,外層也可能收不到原本的錯誤。 function loadStatus() { try { throw new Error('讀取失敗') } finally { return 'fallback' } } try { console.log(loadStatus()) // fallback } catch (error) { console.log('外層收到:', error.message) } 這段會輸出fallback,不會進入外層catch。若你看到程式明明throw了,頁面卻像成功一樣繼續走,就要檢查finally是否有return。需要轉成預設結果時,應把錯誤處理規則寫在明確的catch或業務分支,別讓清理區順手吞掉例外。 把finally留給收尾,讓結果沿原路出去 finally適合恢復狀態或釋放已取得的資源。下面以記錄「收尾完成」代替實際資源操作;收尾區不回傳值,也不另外丟出錯誤。 function readStatusSafely() { try { return 'ready'...
Linux目錄明明很大卻算不出來?du別只看星號展開的檔案
網站目錄佔了不少空間,跑du -sh ./*卻只看到幾個小目錄?先別認定du算錯。在一般Bash設定下,星號展開不會包含名稱以點開頭的項目;像.cache、.git這些目錄,可能根本沒被交給du統計。 先看星號實際展開了什麼 到要檢查的目錄,先確認所在位置,再列出./*代表的路徑。下面只顯示位置與名稱,不會修改檔案。 pwd printf '%s\n' ./* 假設目錄裡有visible和.cache兩個子目錄,Bash未啟用dotglob時,這個星號通常只展開成./visible。接著執行du -sh -- ./*,du收到的也只有那個路徑。.cache沒有出現在結果中,不是du先讀取後把它排除,而是shell沒有把它列進參數。 不同shell或自訂設定可能改變展開結果;若星號沒有匹配到項目,顯示原樣或報錯也可能和設定有關。先看實際路徑,不要把某台主機的習慣當成所有Linux環境的固定行為。 要算整個目前目錄,就直接指定點 想知道目前目錄總共佔多少空間,不必把星號改成一長串隱藏檔案模式。直接把目前目錄.交給du: du -sh -- . -s彙總指定路徑,-h用容易閱讀的單位顯示。du會從這個目錄往下統計,名稱以點開頭的子目錄也在範圍內。輸出最後的點代表整個目前目錄,不是單獨一個普通檔案。 再找出哪一個子目錄較大 在提供GNU du的Linux環境,可把顯示層級限制在下一層。這條仍然以目前目錄為起點,而不是使用星號列出普通名稱。 du -h --max-depth=1 -- . 結果會列出第一層子目錄,以及最後的目前目錄總量;符合範圍的.cache也會顯示。--max-depth=1限制的是顯示深度,不代表只讀一層就能算完:子目錄裡的內容仍需要統計,大型目錄照樣可能花時間與I/O。 這個寫法不會替你把每個第一層普通檔案逐一列出。若某個大檔案直接放在目前目錄,總量會包含它,子目錄那幾行相加卻不一定等於總量。不要把這個差額全當成隱藏目錄。 用小例子對照兩種範圍 假設visible裡放一個4KiB檔案,.cache裡放一個1MiB檔案。一般Bash的./*只列visible,沿用它的du結果就不會反映那個1MiB隱藏檔案;改成du -sh -- .,總量會把兩邊都納入。再用上一節的層級顯示,就能看出.cache比visible大。 這裡的檔案大小只是示例,du通常顯示磁碟配置用量;目錄本身、檔案系統配置方式及工具的單位設定,都可能讓顯示值和檔案內容長度不同。重點是比較同一位置、同一工具下的統計範圍,而不是要求輸出剛好等於兩個數字相加。 數字還是對不上時 留意權限不足的錯誤,掃描期間檔案是否持續變動,以及目錄裡有沒有掛載其他檔案系統。上面的指令沒有要求停在同一檔案系統,不能直接拿它的總量和某一個掛載點的df數字畫等號。GNU選項也不保證適用於BusyBox或macOS的du。 找到大目錄後先確認用途和保留規則。快取、版本庫、上傳檔案或備份需要不同處理方式;名稱以點開頭不代表可以刪。這次先把統計範圍查清楚,再決定是否有需要安排清理。 參考資料 GNU Coreutils手冊:du統計與顯示深度 GNU Bash手冊:檔名展開與dotglob
