戰(zhàn):從管道組合到服務(wù)器故障排查指南)
1. 先別急著背命令清單把命令當(dāng)成跟系統(tǒng)對(duì)話早些年我剛接觸 Linux 命令的時(shí)候干過(guò)一件現(xiàn)在想起來(lái)有點(diǎn)好笑的事找了一份《常用 Linux 命令大全》把 ls、cd、cp、mv 的參數(shù)背得滾瓜爛熟結(jié)果到了真正需要排查問(wèn)題的時(shí)候依然兩眼一摸黑。后來(lái)真正讓我對(duì) Linux 命令開竅的不是某本速查手冊(cè)而是一次在服務(wù)器上排查故障時(shí)的經(jīng)歷——我把一條一條看起來(lái)平平無(wú)奇的命令用管道串了起來(lái)突然意識(shí)到原來(lái)這些命令并不是一份需要死記硬背的清單它們本質(zhì)上是一種向操作系統(tǒng)提問(wèn)的方式。這篇文章不打算給你羅列幾百條命令的用法我更想聊的是怎么把命令用起來(lái)怎么從一條單命令出發(fā)逐步組合成能解決實(shí)際問(wèn)題的信息流水線排查服務(wù)器問(wèn)題時(shí)應(yīng)該按什么思路選擇命令以及那些文檔里通常不會(huì)寫、但實(shí)戰(zhàn)里特別關(guān)鍵的細(xì)節(jié)。適合剛學(xué) Linux、總感覺命令學(xué)了就忘的新手也適合已經(jīng)在日常使用 Linux、但遇到問(wèn)題仍然不知道從何下手的進(jìn)階用戶。1.1 命令是提問(wèn)不是背誦清單我后來(lái)總結(jié)出一個(gè)觀點(diǎn)每一條命令都在回答一個(gè)具體的問(wèn)題。ls 回答這個(gè)目錄里有什么ps 回答現(xiàn)在系統(tǒng)里有哪些進(jìn)程在跑df 回答磁盤空間還剩多少。當(dāng)你把命令理解成提問(wèn)工具而不是要背的單詞學(xué)習(xí)方式自然就變了——遇到陌生命令第一反應(yīng)不再是我又要背一個(gè)了而是這條命令能幫我回答什么疑問(wèn)。順著這個(gè)思路命令后面的選項(xiàng)和參數(shù)也不過(guò)是讓答案更精確的限定條件。比如 ps 這條命令ps aux 和 ps -ef 都能查看進(jìn)程呢但輸出格式不同適合的場(chǎng)景也不同。你不需要把幾十個(gè)選項(xiàng)全部背下來(lái)只需要知道這條命令能回答進(jìn)程狀態(tài)這一類問(wèn)題然后遇到實(shí)際需求時(shí)去查它的用法。查用法有兩條路man 命令名或者 命令名 --help。很多人看到 man 手冊(cè)那么長(zhǎng)就退縮其實(shí)根本不需要從頭讀到尾只看你關(guān)心的那個(gè)選項(xiàng)就行。真正重要的不是把手冊(cè)讀完而是養(yǎng)成先查后試的習(xí)慣。這個(gè)習(xí)慣比任何命令大全都管用因?yàn)橄到y(tǒng)自帶的幫助信息永遠(yuǎn)比別人的筆記更新、更貼合手頭這個(gè)版本。1.2 從最小可運(yùn)行命令開始別一上來(lái)就堆參數(shù)我見過(guò)不少人一上來(lái)就寫特別長(zhǎng)的命令比如 find 配合 -exec、多層管道、一堆選項(xiàng)全部疊加一旦報(bào)錯(cuò)根本不知道是哪一個(gè)環(huán)節(jié)出了問(wèn)題。我的習(xí)慣剛好相反先跑一個(gè)最小版本確認(rèn)命令本身能正常工作輸出格式也符合預(yù)期然后再一點(diǎn)點(diǎn)把過(guò)濾條件、排序、統(tǒng)計(jì)加上去。這個(gè)過(guò)程很像排查電路故障先確認(rèn)總閘有電再逐條支路檢查而不是一上來(lái)就把所有開關(guān)都按一遍。舉個(gè)例子你想用 awk 從日志里取某一列如果上來(lái)就寫 awk {print $2}結(jié)果發(fā)現(xiàn) $2 根本不是你想要的那一列因?yàn)槟銐焊鶝](méi)見過(guò)日志原始長(zhǎng)什么樣。正確做法是先 head -n 5 日志文件看一眼列是怎么排的確認(rèn)第幾列是IP、第幾列是時(shí)間再寫 awk。這個(gè)先看輸出、再動(dòng)手處理的習(xí)慣能幫你省掉大量自我懷疑的時(shí)間。配套的技巧叫小范圍驗(yàn)證處理大文件時(shí)先用 head 或 grep head 把輸入縮小邏輯跑通之后再對(duì)全量數(shù)據(jù)執(zhí)行。尤其是 sed、awk 這類會(huì)修改內(nèi)容的命令強(qiáng)烈建議先在測(cè)試目錄或小樣本上試一遍確認(rèn)結(jié)果符合預(yù)期再正式執(zhí)行。別嫌這一步啰嗦真出問(wèn)題的時(shí)候它會(huì)讓你少說(shuō)很多次完蛋了。這套方法同樣適用于后面要講的管道命令——每次只加一段跑一下確認(rèn)沒(méi)問(wèn)題再繼續(xù)拼。2. 一條排查命令是怎么長(zhǎng)出來(lái)的管道組合實(shí)戰(zhàn)很多人學(xué)命令是一條一條學(xué)的但實(shí)際干活的時(shí)候真正體現(xiàn)功力的地方是把命令用管道串起來(lái)。管道說(shuō)白了就是把前一條命令的輸出交給后一條命令作為輸入。用好了一條命令就能完成一次小規(guī)模的數(shù)據(jù)分析。我拿一個(gè)特別常見的場(chǎng)景來(lái)講某天服務(wù)響應(yīng)變慢懷疑有客戶端在反復(fù)刷某個(gè)接口日志目錄里放著訪問(wèn)日志現(xiàn)在想找出到底哪些 IP 在頻繁請(qǐng)求這個(gè)接口。2.1 場(chǎng)景與需求我要從日志里找到誰(shuí)在刷接口先別急著寫命令第一步是看一眼日志長(zhǎng)什么樣。執(zhí)行 tail -n 20 訪問(wèn)日志路徑確認(rèn)列格式。這一步是整條排查命令的地基因?yàn)楹竺嫒∧囊涣小⒃趺催^(guò)濾完全取決于日志里字段的排列方式。假設(shè)我們看到日志格式大致是IP 時(shí)間 請(qǐng)求方法 請(qǐng)求地址 狀態(tài)碼那 IP 就在第一列。很多人忽略這個(gè)步驟直接 grep 然后 awk {print $1}結(jié)果日志格式稍有變化取到的列根本不是自己想要的東西。這就是沒(méi)看清現(xiàn)場(chǎng)就動(dòng)手的典型失誤。日志格式確認(rèn)之后你的需求才能轉(zhuǎn)成清晰的命令目標(biāo)先過(guò)濾出包含指定接口的日志行再?gòu)倪@些行里提取 IP 字段最后按 IP 去重并統(tǒng)計(jì)請(qǐng)求次數(shù)。每一步對(duì)應(yīng)什么命令這時(shí)就非常自然了。2.2 逐段拼接每加一步都驗(yàn)證一遍我按實(shí)際排查過(guò)程把這條命令拆開你可以看到它是怎么一步步長(zhǎng)出來(lái)的。第一步只篩選出相關(guān)請(qǐng)求grep api/login 訪問(wèn)日志路徑跑完先看輸出是否符合預(yù)期如果行數(shù)太多可以先在末尾加 head -n 10 看幾條樣例。第二步把 IP 字段取出來(lái)grep api/login 訪問(wèn)日志路徑 | awk {print $1}這時(shí)輸出會(huì)是一堆可能重復(fù)的 IP。第三步排序并統(tǒng)計(jì)次數(shù)。這里有一個(gè)很關(guān)鍵的順序問(wèn)題必須先 sort 再 uniq -c。因?yàn)?uniq 默認(rèn)只統(tǒng)計(jì)相鄰的重復(fù)行如果同一個(gè) IP 在文件中是分段出現(xiàn)的直接 uniq -c 會(huì)把一個(gè) IP 統(tǒng)計(jì)成好幾段結(jié)果完全錯(cuò)誤。正確寫法是grep api/login 訪問(wèn)日志路徑 | awk {print $1} | sort | uniq -c到這一步輸出變成次數(shù) IP的形式。第四步按次數(shù)降序排列并只保留前 20 個(gè)結(jié)果grep api/login 訪問(wèn)日志路徑 | awk {print $1} | sort | uniq -c | sort -rn | head -n 20看到這里你應(yīng)該明白了這條命令不是背出來(lái)的而是從一個(gè)疑問(wèn)出發(fā)通過(guò)不斷驗(yàn)證中間輸出自然組合出來(lái)的。每加一個(gè)管道段我都會(huì)先跑一遍看一眼輸出內(nèi)容是不是我想要的再?zèng)Q定下一步往哪邊走。這種逐段驗(yàn)證的拼法比一口氣寫完長(zhǎng)命令要可靠得多。2.3 組合管道時(shí)容易翻車的幾個(gè)細(xì)節(jié)管道好用但有幾個(gè)坑必須提醒。第一個(gè)坑是管道左側(cè)失敗右側(cè)照跑。比如文件不存在時(shí) grep 會(huì)報(bào)錯(cuò)但整個(gè)管道的退出狀態(tài)取決于最后一個(gè)命令。如果你在腳本里啟用了 set -e可能就不會(huì)因?yàn)槲募淮嬖诙袛嗄_本問(wèn)題會(huì)被靜默放過(guò)去后面拿到一堆錯(cuò)誤數(shù)據(jù)時(shí)反而更難排查。第二個(gè)坑是隱藏字符。有些日志文件里帶著 \r 回車符或者亂碼輸出看起來(lái)一切正常但 sort 和 uniq 的結(jié)果就是不符合預(yù)期。遇到可疑情況可以用 cat -A 文件名 | head 看一眼文件中是否藏著看不見的控制字符再?zèng)Q定要不要用 tr 或 sed 先清理。第三個(gè)坑是 awk 列號(hào)寫死的問(wèn)題。如果日志里大部分行有 7 列但偶爾幾行因?yàn)楦袷疆惓V挥?5 列取 $1 可能還算穩(wěn)定取 $4 就可能錯(cuò)位。處理這類臟數(shù)據(jù)先分幾行看一看字段結(jié)構(gòu)差異再選擇取列方式。我的建議是拼管道命令時(shí)每加一段就跑一遍確認(rèn)中間結(jié)果正常再繼續(xù)往后面接。這個(gè)習(xí)慣幫我在排錯(cuò)時(shí)節(jié)省了大量時(shí)間比任何命令速查表都實(shí)在。3. 資源狀態(tài)排查進(jìn)程、內(nèi)存、磁盤的真實(shí)場(chǎng)景命令只有放到真實(shí)場(chǎng)景里反復(fù)用才會(huì)真正變成自己的東西。服務(wù)器出問(wèn)題的時(shí)候其實(shí)是學(xué)習(xí)命令最好的機(jī)會(huì)。這一節(jié)我挑三個(gè)高頻場(chǎng)景講一下我是怎么看狀態(tài)、怎么快速把問(wèn)題定位到具體方向的。3.1 進(jìn)程ps aux 只是起點(diǎn)/proc 里才是真正的細(xì)節(jié)ps aux 大家都會(huì)用但實(shí)際排查時(shí)有幾個(gè)細(xì)節(jié)很容易被忽略。首先ps aux 默認(rèn)按 PID 排序如果想找 CPU 或內(nèi)存占用最高的進(jìn)程要么用 sort 輔助要么進(jìn) top 再按對(duì)應(yīng)列排序。其次ps aux 能看到進(jìn)程的基本狀態(tài)但看不到這個(gè)進(jìn)程的完整啟動(dòng)命令、當(dāng)前工作目錄、環(huán)境變量以及它打開了哪些文件。這些細(xì)節(jié)要去 /proc/PID 目錄里挖。比如某個(gè)進(jìn)程 CPU 占用很高你想知道它到底在干什么可以先看 /proc/PID/cmdline 拿到完整的啟動(dòng)參數(shù)再看 /proc/PID/status 里的線程數(shù)和狀態(tài)信息。如果懷疑進(jìn)程打開了某個(gè)文件不釋放可以用 lsof -p PID 查看它持有的文件句柄。說(shuō)實(shí)話剛接觸 Linux 的人會(huì)覺得 /proc 下一堆數(shù)字目錄很怪但用熟了以后會(huì)發(fā)現(xiàn)它幾乎是系統(tǒng)主動(dòng)敞開的一扇觀察窗口。補(bǔ)充一個(gè)實(shí)用命令想按內(nèi)存占用排序找進(jìn)程可以直接寫 ps aux --sort-%mem | head -n 10一步到位。不過(guò)要提醒一點(diǎn)進(jìn)程的 RSS 內(nèi)存包含它加載的共享庫(kù)占用的那部分把所有進(jìn)程的 RSS 簡(jiǎn)單相加來(lái)估算系統(tǒng)總內(nèi)存需求是不準(zhǔn)確的這一點(diǎn)初學(xué)者非常容易誤解。3.2 磁盤df 滿了du 卻找不到大文件磁盤滿了但不知道是誰(shuí)占的這個(gè)場(chǎng)景幾乎每個(gè)運(yùn)維和開發(fā)者都遇到過(guò)。df 和 du 的視角不一樣df 看的是文件系統(tǒng)整體用了多少du 看的是某個(gè)目錄下所有文件實(shí)際占用多少。如果 df -h 顯示根分區(qū)已經(jīng) 100%但到根目錄執(zhí)行 du -sh * 逐項(xiàng)相加后發(fā)現(xiàn)遠(yuǎn)小于總量說(shuō)明一定有什么東西在看不見地占著空間。最常見的元兇有兩個(gè)。第一個(gè)是已被刪除但還被進(jìn)程占用的文件進(jìn)程打開了一個(gè)文件隨后文件被刪除但進(jìn)程沒(méi)有關(guān)閉句柄磁盤空間就不會(huì)釋放。df 能看到空間沒(méi)了du 卻無(wú)論如何也找不到這個(gè)大文件。這種情況用 lsof | grep deleted 就能把它揪出來(lái)。第二個(gè)是 inode 耗盡小文件太多把索引節(jié)點(diǎn)用完了df 顯示空間還有剩余但新文件就是創(chuàng)建不出來(lái)。這時(shí)候要執(zhí)行 df -i 看 inode 使用率。很多人只盯 df -h不看 df -i所以遇到空間夠但寫不進(jìn)文件這種詭異問(wèn)題就會(huì)卡住。還有一種情況是目錄被重復(fù)掛載。一個(gè)目錄被掛到兩個(gè)位置du 默認(rèn)會(huì)跨掛載點(diǎn)重復(fù)計(jì)算導(dǎo)致統(tǒng)計(jì)結(jié)果虛高。如果只想看當(dāng)前文件系統(tǒng)自己的用量給 du 加上 -x 參數(shù)讓它不跨越文件系統(tǒng)邊界。以后再遇到df 和 du 對(duì)不上時(shí)別急著懷疑命令先按這三條逐個(gè)排查思路會(huì)清楚很多。3.3 內(nèi)存free 里的 buffer/cache 不是占用執(zhí)行 free -h 的時(shí)候很多新手看到 used 很高、buff/cache 也很大就開始慌了覺得內(nèi)存是不是快不夠了。實(shí)際上buff/cache 是 Linux 的一種正常內(nèi)存管理機(jī)制把暫時(shí)用不上的空閑內(nèi)存拿去做文件緩存等應(yīng)用程序需要更多內(nèi)存時(shí)這些緩存會(huì)被隨時(shí)釋放回來(lái)。真正判斷內(nèi)存夠不夠要看的是 available 這一列它表示在不發(fā)生 swap 的情況下還能分配給新進(jìn)程多少內(nèi)存。判斷內(nèi)存壓力的信號(hào)有這幾個(gè)swap 使用率持續(xù)上漲、系統(tǒng)日志里出現(xiàn) OOM 相關(guān)的記錄、top 里某些進(jìn)程被莫名其妙殺掉。遇到這些情況再去查到底是哪個(gè)進(jìn)程占得多比直接升級(jí)內(nèi)存更有效。我之前處理過(guò)類似場(chǎng)景一個(gè)服務(wù)平時(shí)內(nèi)存占用正常但每到某個(gè)固定時(shí)間點(diǎn)就掛一次后來(lái)翻日志發(fā)現(xiàn)是定時(shí)任務(wù)和主服務(wù)同時(shí)跑內(nèi)存峰值超標(biāo)了。整個(gè)過(guò)程用到的命令也就是 free、top、journalctl 這些但核心在于是否愿意順著現(xiàn)象多問(wèn)幾層為什么而不是停留在重啟一下試試。4. 少跑冤枉路的排查思路從現(xiàn)象到根因命令只是工具思路決定你花多長(zhǎng)時(shí)間解決問(wèn)題。不少人在排查時(shí)喜歡病急亂投醫(yī)這條命令不行換那條最后直接重啟大法收?qǐng)?。其?shí)只要按層次逐步排除很多問(wèn)題都能在幾分鐘內(nèi)鎖定到很小的范圍。這一節(jié)我想分享的是我在實(shí)際排錯(cuò)中沉淀下來(lái)的分層思考框架。4.1 先確認(rèn)現(xiàn)象在哪一層再?zèng)Q定用什么命令一個(gè)服務(wù)訪問(wèn)不了可能性實(shí)際上非常多客戶端 DNS 解析失敗、網(wǎng)絡(luò)不通、服務(wù)端口沒(méi)監(jiān)聽、服務(wù)進(jìn)程掛了、防火墻規(guī)則攔截、后端數(shù)據(jù)庫(kù)連不上、應(yīng)用代碼本身報(bào)錯(cuò)。如果一上來(lái)就 ping 網(wǎng)關(guān)很可能把時(shí)間浪費(fèi)在錯(cuò)誤的方向上。我的習(xí)慣是把問(wèn)題分四層看應(yīng)用層、傳輸層、網(wǎng)絡(luò)層、系統(tǒng)層按順序逐層驗(yàn)證。第一層先在服務(wù)器本機(jī)測(cè)應(yīng)用層用 curl 訪問(wèn)本機(jī)地址如果服務(wù)本身和端口都正常curl 應(yīng)該很快返回業(yè)務(wù)響應(yīng)。第二層從內(nèi)網(wǎng)或外部機(jī)器訪問(wèn)看是不是網(wǎng)絡(luò)路徑或防火墻攔截的問(wèn)題。第三層查服務(wù)日志確認(rèn)是業(yè)務(wù)邏輯報(bào)錯(cuò)還是資源不足。每一層都用一兩個(gè)命令去驗(yàn)證然后縮小范圍。比如 curl 本機(jī)通、外部不通問(wèn)題大概率在網(wǎng)絡(luò)層或防火墻本機(jī) curl 都不通那就直接往進(jìn)程和端口方向查。這套思路聽著簡(jiǎn)單但實(shí)際很多人遇到問(wèn)題的第一個(gè)動(dòng)作就是重啟服務(wù)。重啟確實(shí)能讓問(wèn)題暫時(shí)消失但也會(huì)把現(xiàn)場(chǎng)破壞掉——進(jìn)程狀態(tài)、網(wǎng)絡(luò)連接、日志上下文全部重置之后再想復(fù)盤根因就非常被動(dòng)了。我的原則是重啟之前至少先保存日志、記錄進(jìn)程狀態(tài)、記下時(shí)間線這三樣?xùn)|西。保住現(xiàn)場(chǎng)比急著恢復(fù)服務(wù)更有利于找到真因。4.2 網(wǎng)絡(luò)類命令的高頻組合與正確用法網(wǎng)絡(luò)排查的命令本身不難難的是理解每一條命令到底能證明什么。ping 通了只能說(shuō)明 ICMP 這一層通不能證明業(yè)務(wù)端口是通的端口能連上說(shuō)明 TCP 握手成功但也不代表應(yīng)用層正常。所以我常用的組合是先 ping 看基礎(chǔ)連通性再 ss -tlnp 看本機(jī)端口是否在監(jiān)聽然后用 nc 從另一端測(cè)一下端口連通性最后用 curl -v 帶超時(shí)參數(shù)去測(cè)應(yīng)用層響應(yīng)。每一條命令回答的層都不同組合起來(lái)才能完成定位。舉一個(gè)實(shí)際的排查過(guò)程某次遇到網(wǎng)站間歇性打不開我在服務(wù)器本機(jī)用 curl 訪問(wèn)一切正常但從外部訪問(wèn)就超時(shí)。接下來(lái)用 nc -vz 目標(biāo)IP 端口 測(cè)端口發(fā)現(xiàn) TCP 層也通不過(guò)于是基本判斷問(wèn)題出在網(wǎng)絡(luò)路徑或防火墻。再檢查防火墻規(guī)則和安全組配置很快就找到了原因。全程沒(méi)有用任何復(fù)雜的命令但每一條都完成了排除一個(gè)方向的任務(wù)。這里有個(gè)容易被忽略的細(xì)節(jié)nc、mtr、dig 這類命令在部分精簡(jiǎn)安裝的 Linux 上默認(rèn)不存在。在網(wǎng)絡(luò)問(wèn)題排查之前先確認(rèn)系統(tǒng)里有沒(méi)有這些工具缺什么就裝什么否則查了半天才發(fā)現(xiàn)命令根本不存在節(jié)奏就被完全打亂了。另外curl 的 -w 參數(shù)可以輸出連接耗時(shí)、總時(shí)間等指標(biāo)用來(lái)判斷慢在哪一段尤其好用值得專門花兩分鐘熟悉一下。4.3 日志時(shí)間線別只盯著最新幾行日志是排查問(wèn)題最直接的現(xiàn)場(chǎng)記錄但很多人只會(huì) tail -n 100 看最近幾行。問(wèn)題如果發(fā)生在幾小時(shí)前等你發(fā)現(xiàn)并登錄服務(wù)器的時(shí)候關(guān)鍵日志可能早就被新內(nèi)容沖掉了。正確的做法是先確定問(wèn)題是從什么時(shí)候開始出現(xiàn)的然后把那個(gè)時(shí)間窗口附近的日志完整拉出來(lái)看。目前我用的比較多的方式是 journalctl 這套執(zhí)行 journalctl -u 服務(wù)名 --since 2 hours ago 可以看某個(gè)服務(wù)最近兩個(gè)小時(shí)的日志還能用 --until 進(jìn)一步收窄時(shí)間窗口。接著用 grep 按關(guān)鍵詞過(guò)濾比如搜 OOM、error、timeout然后再順著時(shí)間線把上下文完整看一遍。很多時(shí)候真正的根因藏在錯(cuò)誤日志前面幾十行里只看孤零零的一句報(bào)錯(cuò)很容易被誤導(dǎo)到錯(cuò)誤的方向。如果系統(tǒng)沒(méi)有啟用 journalctl日志只寫在傳統(tǒng)文件里可以先看日志文件的修改時(shí)間來(lái)判斷問(wèn)題的大致時(shí)間點(diǎn)再?gòu)哪莻€(gè)時(shí)間點(diǎn)附近取日志。無(wú)論用哪種方式核心方法是一致的把錯(cuò)誤發(fā)生時(shí)刻前后幾分鐘的信息放到一起看。排查問(wèn)題要看時(shí)間線而不是只看單個(gè)報(bào)錯(cuò)點(diǎn)這是我跟很多人反復(fù)強(qiáng)調(diào)的一點(diǎn)。5. 被默認(rèn)忽略的命令習(xí)慣安全與效率兼得最后想聊的是命令使用習(xí)慣。這部分很少有人專門教但踩過(guò)的坑往往最疼。我總結(jié)了幾條可以直接照做的經(jīng)驗(yàn)?zāi)軒湍闵僮吆芏鄰澛贰?.1 rm 這類危險(xiǎn)操作給自己上個(gè)保險(xiǎn)rm -rf 是很多人心中的噩夢(mèng)。最常見的風(fēng)險(xiǎn)來(lái)源是這幾個(gè)路徑變量為空、路徑拼寫錯(cuò)誤、在錯(cuò)誤的目錄下執(zhí)行。我的建議有三條。第一把 rm 替換成帶回收站功能的方式比如設(shè)置 alias rmtrash-put或者寫一個(gè)自定義函數(shù)把刪除的文件移動(dòng)到一個(gè)專門的回收目錄里這樣至少還有后悔藥可以吃。第二如果必須在腳本里用 rm先判斷變量是否為非空比如 [[ -n $目錄變量 ]] 確認(rèn)后再繼續(xù)操作。第三執(zhí)行刪除之前先 pwd 確認(rèn)當(dāng)前目錄再 ls 確認(rèn)目標(biāo)路徑。有人覺得這些習(xí)慣太啰嗦但真出過(guò)一次事就會(huì)明白多花十秒鐘確認(rèn)比花一整晚恢復(fù)數(shù)據(jù)劃算太多。尤其是在生產(chǎn)環(huán)境里rm 的安全習(xí)慣應(yīng)該變成肌肉記憶不靠運(yùn)氣靠流程。5.2 history、alias、配置把常用命令沉淀成自己的裝備命令用好之后很多人還是每次重復(fù)敲一長(zhǎng)串效率很低。我習(xí)慣把高頻組合寫成別名比如 alias llls -lh、alias ggrep、alias ..cd ..寫進(jìn) ~/.bashrc 或 ~/.zshrc 之后日常操作會(huì)舒服很多。但這里有一個(gè)經(jīng)典坑要提醒在腳本里不要依賴別名。非交互式 shell 默認(rèn)不會(huì)展開 alias你明明配置了 alias ggrep在腳本里寫 g abc file 卻可能報(bào)命令找不到這個(gè)問(wèn)題會(huì)讓不少人困惑很久。歷史記錄也值得好好配置。默認(rèn)情況下 history 只記錄命令本身不記錄時(shí)間長(zhǎng)度也有限。我通常會(huì)在配置里加這幾行HISTTIMEFORMAT%F %T HISTSIZE10000 HISTFILESIZE20000加上之后history 輸出會(huì)帶上時(shí)間戳復(fù)盤問(wèn)題的時(shí)候會(huì)非常有用。還有一條安全習(xí)慣必須強(qiáng)調(diào)不要把密碼、密鑰、token 直接寫在命令行里因?yàn)?history 會(huì)記錄腳本里也會(huì)留痕。如果確實(shí)需要應(yīng)該通過(guò)環(huán)境變量或配置文件讀取不要圖省事直接明文寫在命令里。5.3 重定向、通配符、sudo 的常見坑重定向是新手最容易出問(wèn)題的地方。第一點(diǎn)是 會(huì)覆蓋文件 會(huì)追加文件第二點(diǎn)是 21 表示把標(biāo)準(zhǔn)錯(cuò)誤重定向到標(biāo)準(zhǔn)輸出當(dāng)前指向的位置這兩者順序不同結(jié)果也完全不同。一個(gè)非常經(jīng)典的錯(cuò)誤是執(zhí)行 sudo echo 1 /某個(gè)root屬主的文件結(jié)果報(bào)權(quán)限不足因?yàn)橹囟ㄏ蚴钱?dāng)前 shell 打開的sudo 只作用于 echo 命令本身。正確做法是 echo 1 | sudo tee /某個(gè)root屬主的文件或者 sudo bash -c echo 1 /某個(gè)root屬主的文件。通配符也有一個(gè)容易踩的坑。rm.log 在沒(méi)有匹配文件時(shí).log 這個(gè)字面字符串會(huì)原樣傳給 rm然后 rm 報(bào)錯(cuò)文件不存在。更危險(xiǎn)的情況是如果目錄里恰好有一個(gè)文件叫 *.log這個(gè)命令會(huì)把它也刪掉。要避免這類意外可以寫 rm -- *.log雙橫線表示后面的參數(shù)都作為文件名處理。通配符本質(zhì)上是 shell 先做展開再交給命令執(zhí)行理解了這個(gè)機(jī)制你才能控制它而不是被它坑到。最后說(shuō)一句 sudo 的使用邊界。很多人習(xí)慣遇到權(quán)限問(wèn)題就一長(zhǎng)串 sudo但這會(huì)掩蓋真實(shí)的權(quán)限問(wèn)題。能用普通用戶執(zhí)行就不要用 root 身份能限制在單條命令上就不要開一個(gè) root shell。這句話聽起來(lái)像老生常談但生產(chǎn)環(huán)境里權(quán)限最小化是真的能兜底的。根據(jù)自己的經(jīng)驗(yàn)我最后分享一個(gè)小習(xí)慣每周挑一條平時(shí)用得少但知道很有用的命令比如 find、xargs、jq花十分鐘讀一遍它的手冊(cè)在一個(gè)測(cè)試目錄里用真實(shí)數(shù)據(jù)玩一遍然后思考它還能怎么跟別的命令組合。堅(jiān)持一個(gè)月你會(huì)發(fā)現(xiàn)自己能寫出來(lái)的命令結(jié)構(gòu)越來(lái)越豐富。Linux 命令這東西背了不用等于沒(méi)學(xué)用了并且認(rèn)真思考過(guò)才真正變成你自己的。