命令列的藝術
流暢地使用命令列是一個常被忽略的技能,或被認為是神祕的奧義。但是,它會以明顯而微妙的方式改善你作為工程師的靈活度和生產力。這是我在 Linux 上工作時發現的有用的命令列使用小竅門和筆記的精粹。有些小竅門是很基礎的,而有些是相當地特別、複雜、或者晦澀難懂。這篇文章不長,但是如果你可以使用並記得這裡的所有內容,那麼你就懂得很多了。
其中大部分最初出現在Quora上,但是考慮到興趣所在,似乎更應該放到 Github 上,這裡的人比我更能提出改進建議。如果你看到一個錯誤,或者更好的某種東西,請提交問題或 PR!(當然,提交前請看看必讀小節和已有的 PR/Issue。)
必讀
範圍:
- 本文是針對初學者和專業人員的,選題目標是覆蓋面廣(全都很重要)、有針對性(大多數情況下都給出具體例項)而簡潔(避免不必要內容以及你能在其它地方輕鬆找到的離題的內容)。每個小竅門在某種情形下都很必需的,或者能比替代品大大節省時間。
- 這是為 Linux 寫的。絕大部分條目都可以同樣應用到 MacOS(或者甚至 Cygwin)。
- 主要針對互動式 Bash,儘管大多數小竅門也可以應用到其它 shell,以及常規 Bash 指令碼。
- 包括了“標準的”UNIX 命令以及那些需要安裝的軟體包(它們很重要,值得安裝)。
注意:
- 為了能在一篇文章內展示儘量多的東西,一些具體的資訊會被放到引用頁裡。你可以使用 Google 來獲得進一步的內容。(如果需要的話,)你可以使用 apt-get/yum/dnf/pacman/pip/brew 來安裝這些新的程式。
- 使用 Explainshell 來獲取命令、引數、管道等內容的解釋。
基礎
- 學習基本 Bash 技能。實際上,鍵入man bash,然後至少瀏覽一遍所有內容;它很容易理解,沒那麼長。其它 shell 也不錯,但是 Bash 很強大,而且到處都可以找到(如果在你自己的筆記本上只學習 zsh、fish 之類,會在很多情形下受到限制,比如使用現存的伺服器時)。
- 至少學好一種基於文字的編輯器。理想的一個是 Vim(vi),因為在終端中編輯時隨時都能找到它(即使大多數時候你在使用 Emacs、一個大型的 IDE、或一個現代的時髦編輯器)。
- 學習怎樣使用 man 來閱讀文件(好奇的話,用 man man 來列出分割槽號,比如 1 是常規命令,5 是檔案描述,8 用於管理員)。用 apropos 找到幫助頁。瞭解哪些命令不是可執行程式,而是 Bash 內建的,你可以用 help 和 help -d 得到幫助。
- 學習使用 > 和 < 來進行輸出和輸入重定向,以及使用 | 來管道重定向,學習關於 stdout 和 stderr 的東西。
- 學習 *(也許還有 ? 和 {…} )檔案通配擴充套件和應用,以及雙引號 ” 和單引號 ‘ 之間的區別。(更多內容請參看下面關於變數擴充套件部分)。
- 熟悉 Bash 作業管理:&, ctrl-z, ctrl-c, jobs, fg, bg, kill 等等。
- 掌握ssh,以及通過 ssh-agent,ssh-add 等進行無密碼驗證的基礎技能。
- 基本的檔案管理:ls 和 ls -l(特別是,知道ls -l各個列的意義),less, head, tail 和 tail -f(或者更好的less +F),ln 和 ln -s(知道硬連結和軟連結的區別,以及硬連結相對於軟連結的優勢),chown,chmod,du(用於檢視磁碟使用率的快速摘要:du -sk *)。檔案系統管理:df, mount,fdisk,mkfs,lsblk。
- 基本的網路管理: ip 或 ifconfig,dig。
- 熟知正規表示式,以及各種使用grep/egrep的選項。-i,-o,-A 和 -B 選項值得掌握。
- 學會使用 apt-get,yum ,dnf 或 pacman(這取決於你的發行版)來查詢並安裝軟體包。確保你可以用 pip 來安裝基於 Python 的命令列工具(下面的一些東西可以很容易地通過 pip 安裝)。
日常使用
- 在Bash中,使用 tab 補完引數,使用 ctrl-r 來搜尋命令歷史。
- 在Bash中,使用 ctrl-w 來刪除最後的單詞,使用 ctrl-u 來刪除整行,返回行首。使用 alt-b 和 alt-f 來逐詞移動,使用 ctrl-k 來清除到行尾的內容,以及使用 ctrl-l 清屏。參見 man readline 來檢視 Bash 中所有預設的鍵盤繫結,有很多。例如,alt-. 可以迴圈顯示先前的引數,而alt- 擴充套件通配。
- 另外,如果你喜歡 vi 風格的鍵盤繫結,可以使用 set -o vi。
- 要檢視最近用過的命令,請使用 history 。 有許多縮寫形式,比如 !$(上次的引數)和!!(上次的命令),雖然使用 ctrl-r 和 alt-. 更容易些。
- 返回先前的工作目錄: cd -
- 如果你命令輸入到一半,但是改變主意了,可以敲 alt-# 來新增一個 # 到開頭,然後將該命令作為註釋輸入(或者使用快捷鍵 ctrl-a, #,enter 輸入)。然後,你可以在後面通過命令歷史來回到該命令。
- 使用 xargs(或 parallel),它很強大。注意,你可以控制每行(-L)執行多少個專案,以及並行執行(-P)。如果你不確定它是否會做正確的事情,可以首先使用 xargs echo。同時,使用 -I{} 也很方便。樣例:
find . -name '*.py' | xargs grep some_function cat hosts | xargs -I{} ssh root@{} hostname
- pstree -p 對於顯示程式樹很有幫助。
- 使用 pgrep 和 pkill 來按名稱查詢程式或給指定名稱的程式傳送訊號(-f 很有幫助)。
- 掌握各種可以傳送給程式的訊號。例如,要掛起程式,可以使用 kill -STOP [pid]。完整的列表可以查閱 man 7 signal。
- 如果你想要一個後臺程式一直保持執行,使用 nohup 或 disown。
- 通過 netstat -lntp 或 ss -plat 檢查哪些程式在監聽(用於 TCP,對 UDP 使用 -u 替代 -t)。
- lsof來檢視開啟的套接字和檔案。
- 在 Bash 指令碼中,使用 set -x 除錯指令碼輸出。儘可能使用嚴格模式。使用 set -e 在遇到錯誤時退出。也可以使用 set -o pipefail,對錯誤進行嚴格處理(雖然該話題有點微妙)。對於更復雜的指令碼,也可以使用 trap。
- 在 Bash 指令碼中,子 shell(寫在括號中的)是組合命令的便利的方式。一個常見的例子是臨時移動到一個不同的工作目錄,如:
# 在當前目錄做些事 (cd /some/other/dir; other-command) # 繼續回到原目錄
- 注意,在 Bash 中有大量的各種各樣的變數擴充套件。檢查一個變數是否存在:${name:?error message}。例如,如果一個Bash指令碼要求一個單一引數,只需寫 input_file=${1:?usage: $0 input_file}。算術擴充套件:i=$(( (i + 1) % 5 ))。序列: {1..10}。修剪字串:${var%suffix} 和 ${var#prefix}。例如,if var=foo.pdf ,那麼 echo ${var%.pdf}.txt 會輸出 foo.txt。
- 命令的輸出可以通過 <(some command) 作為一個檔案來處理。例如,將本地的 /etc/hosts 和遠端的比較:
diff /etc/hosts <(ssh somehost cat /etc/hosts)
- 瞭解 Bash 中的“嵌入文件”,就像在 cat <<EOF … 中。
- 在 Bash 中,通過:some-command >logfile 2>&1 同時重定向標準輸出和標準錯誤。通常,要確保某個命令不再為標準輸入開啟檔案控制程式碼,而是將它捆綁到你所在的終端,新增 </dev/null 是個不錯的做法。
- man ascii 可以得到一個不錯的ASCII表,帶有十六進位制和十進位制值兩種格式。對於常規編碼資訊,man unicode,man utf-8 和 man latin1 將很有幫助。
- 使用 screen 或 tmux 來複用螢幕,這對於遠端 ssh 會話尤為有用,使用它們來分離並重連到會話。另一個只用於保持會話的最小可選方案是 dtach。
- 在 ssh 中,知道如何使用 -L 或 -D(偶爾也用-R)來開啟埠通道是很有用的,如從一臺遠端伺服器訪問網站時。
- 為你的 ssh 配置進行優化很有用;例如,這個 ~/.ssh/config 包含了可以避免在特定網路環境中連線被斷掉的情況的設定、使用壓縮(這對於通過低頻寬連線使用 scp 很有用),以及使用一個本地控制檔案來開啟到同一臺伺服器的多通道:
TCPKeepAlive=yes ServerAliveInterval=15 ServerAliveCountMax=6 Compression=yes ControlMaster auto ControlPath /tmp/%r@%h:%p ControlPersist yes
- 其它一些與 ssh 相關的選項對會影響到安全,請小心開啟,如各個子網或主機,或者在信任的網路中:StrictHostKeyChecking=no, ForwardAgent=yes
- 要獲得八進位制格式的檔案的許可權,這對於系統配置很有用而用 ls 又沒法檢視,而且也很容易搞得一團糟,可以使用像這樣的東西:
stat -c '%A %a %n' /etc/timezone
- 對於從另一個命令的輸出結果中互動選擇值,可以使用percol。
- 對於基於另一個命令(如git)輸出的檔案互動,可以使用fpp (路徑選擇器)。
- 要為當前目錄(及子目錄)中的所有檔案構建一個簡單的 Web 伺服器,讓網路中的任何人都可以獲取,可以使用: python -m SimpleHTTPServer 7777 (使用埠 7777 和 Python 2)。
處理檔案和資料
- 要在當前目錄中按名稱定位檔案,find . -iname ‘*something*’(或者相類似的)。要按名稱查詢任何地方的檔案,使用 locate something(但請記住,updatedb 可能還沒有索引最近建立的檔案)。
- 對於原始碼或資料檔案進行的常規搜尋(要比 grep -r 更高階),使用 ag。
- 要將 HTML 轉成文字:lynx -dump -stdin。
- 對於 Markdown、HTML,以及各種型別的文件轉換,可以試試 pandoc。
- 如果你必須處理 XML,xmlstarlet 雖然有點老舊,但是很好用。
- 對於 JSON,使用jq。
- 對於 Excel 或 CSV 檔案,csvkit 提供了 in2csv,csvcut,csvjoin,csvgrep 等工具。
- 對於亞馬遜 S3 ,s3cmd 會很方便,而 s4cmd 則更快速。亞馬遜的 aws 則是其它 AWS 相關任務的必備。
- 掌握 sort 和 uniq,包括 uniq 的 -u 和 -d 選項——參見下面的單行程式。
- 掌握 cut,paste 和 join,它們用於處理文字檔案。很多人會使用 cut,但常常忘了 join。
- 瞭解 tee,它會將 stdin 同時複製到一個檔案和 stdout,如 ls -al | tee file.txt。
- 知道 locale 會以微妙的方式對命令列工具產生大量的影響,包括排序的順序(整理)以及效能。大多數安裝好的 Linux 會設定 LANG 或其它 locale 環境變數為本地設定,比如像 US English。但是,你要明白,如果改變了本地環境,那麼排序也將改變。而且 i18n 過程會讓排序或其它命令的執行慢好多倍。在某些情形中(如像下面那樣的設定操作或唯一性操作),你可以安全地整個忽略緩慢的 i18n 過程,然後使用傳統的基於位元組的排序順序 export LC_ALL=C。
- 瞭解基本的改動資料的 awk 和 sed 技能。例如,計算某個文字檔案第三列所有數字的和:awk ‘{ x += $3 } END { print x }’。這可能比 Python 的同等操作要快3倍,而且要短3倍。
- 在一個或多個檔案中,替換所有出現在特定地方的某個字串:
perl -pi.bak -e 's/old-string/new-string/g' my-files-*.txt
- 要立即根據某個模式對大量檔案重新命名,使用 rename。對於複雜的重新命名,repren 可以幫助你達成。
# 恢復備份檔案 foo.bak -> foo: rename 's//.bak$//' *.bak # 完整的檔名、目錄名 foo -> bar: repren --full --preserve-case --from foo --to bar .
- 使用 shuf 來從某個檔案中打亂或隨機選擇行。
- 瞭解 sort 的選項。知道這些鍵是怎麼工作的(-t和-k)。特別是,注意你需要寫-k1,1來只通過第一個欄位排序;-k1意味著根據整行排序。
- 穩定排序(sort -s)會很有用。例如,要首先按欄位2排序,然後再按欄位1排序,你可以使用 sort -k1,1 | sort -s -k2,2
- 如果你曾經需要在 Bash 命令列中寫一個水平製表符(如,用於 -t 引數的排序),按ctrl-v [Tab],或者寫$’/t’(後面的更好,因為你可以複製/貼上)。
- 對原始碼進行補丁的標準工具是 diff 和 patch。 用 diffstat 來統計 diff 情況。注意 diff -r 可以用於整個目錄,所以可以用 diff -r tree1 tree2 | diffstat 來統計(兩個目錄的)差異。
- 對於二進位制檔案,使用 hd 進行簡單十六進位制轉儲,以及 bvi 用於二進位制編輯。
- 還是用於二進位制檔案,strings(加上 grep 等)可以讓你找出一點文字。
- 對於二進位制檔案的差異(delta 壓縮),可以使用 xdelta3。
- 要轉換文字編碼,試試 iconv 吧,或者對於更高階的用途使用 uconv;它支援一些高階的 Unicode 的東西。例如,這個命令可以轉換為小寫並移除所有重音符號(通過擴充套件和丟棄):
uconv -f utf-8 -t utf-8 -x '::Any-Lower; ::Any-NFD; [:Nonspacing Mark:] >; ::Any-NFC; ' < input.txt > output.txt
- 要將檔案分割成幾個部分,來看看 split(按大小分割)和 csplit(按格式分割)吧。
- 使用 zless,zmore,zcat 和 zgrep 來操作壓縮檔案。
系統除錯
- 對於 Web 除錯,curl 和 curl -I 很方便靈活,或者也可以使用它們的同行 wget,或者更現代的 httpie。
- 要了解磁碟、CPU、網路的狀態,使用 iostat,netstat,top(或更好的 htop)和(特別是)dstat。它們對於快速獲知系統中發生的狀況很好用。
- 對於更深層次的系統總覽,可以使用 glances。它會在一個終端視窗中為你呈現幾個系統層次的統計資料,對於快速檢查各個子系統很有幫助。
- 要了解記憶體狀態,可以執行 free 和 vmstat,看懂它們的輸出結果吧。特別是,要知道“cached”值是Linux核心為檔案快取所佔有的記憶體,因此,要有效地統計“free”值。
- Java 系統除錯是一件截然不同的事,但是對於 Oracle 系統以及其它一些 JVM 而言,不過是一個簡單的小把戲,你可以執行 kill -3 <pid>,然後一個完整的堆疊追蹤和記憶體堆的摘要(包括常規的垃圾收集細節,這很有用)將被轉儲到stderr/logs。
- 使用 mtr 作路由追蹤更好,可以識別網路問題。
- 對於檢視磁碟滿載的原因,ncdu 會比常規命令如 du -sh * 更節省時間。
- 要查詢佔用頻寬的套接字和程式,試試 iftop 或 nethogs 吧。
- (Apache附帶的)ab工具對於臨時應急檢查網路伺服器效能很有幫助。對於更復雜的負載測試,可以試試 siege。
- 對於更仔細的網路除錯,可以用 wireshark,tshark 或 ngrep。
- 掌握 strace 和 ltrace。如果某個程式失敗、掛起或崩潰,而你又不知道原因,或者如果你想要獲得效能的大概資訊,這些工具會很有幫助。注意,分析選項(-c)和使用 -p 關聯執行程式。
- 掌握 ldd 來檢視共享庫等。
- 知道如何使用 gdb 來連線到一個執行著的程式並獲取其堆疊追蹤資訊。
- 使用 /proc。當除錯當前的問題時,它有時候出奇地有幫助。樣例:/proc/cpuinfo,/proc/xxx/cwd,/proc/xxx/exe,/proc/xxx/fd/,/proc/xxx/smaps。
- 當除錯過去某個東西為何出錯時,sar 會非常有幫助。它顯示了 CPU、記憶體、網路等的歷史統計資料。
- 對於更深層的系統和效能分析,看看 stap (SystemTap),perf) 和 sysdig 吧。
- 確認是正在使用的 Linux 發行版版本(支援大多數發行版):lsb_release -a。
- 每當某個東西的行為異常時(可能是硬體或者驅動器問題),使用dmesg。
單行程式
這是將命令連成一行的一些樣例:
- 有時候通過 sort/uniq 對文字檔案做交集、並集和差集運算時,這個例子會相當有幫助。假定 a 和 b 是已經進行了唯一性處理的文字檔案。這會很快,而且可以處理任意大小的檔案,總計可達數千兆位元組。(Sort不受記憶體限制,不過如果 /tmp 放在一個很小的根分割槽的話,你可能需要使用 -T 選項。)也可參見上面關於LC_ALL的註解和 -u 選項(參見下面例子更清晰)。
sh cat a b | sort | uniq > c # c 是 a 和 b 的並集 cat a b | sort | uniq -d > c # c 是 a 和 b 的交集 cat a b b | sort | uniq -u > c # c 是 a 減去 b 的差集
- 使用 grep . * 來視覺化檢視一個目錄中的所有檔案的所有內容,例如,對於放滿配置檔案的目錄: /sys, /proc, /etc。
- 對某個文字檔案的第三列中所有資料進行求和(該例子可能比同等功能的Python要快3倍,而且程式碼也少於其3倍):
awk '{ x += $3 } END { print x }' myfile
- 如果想要檢視某個檔案樹的大小/日期,該例子就像一個遞迴ls -l,但是比ls -lR要更容易讀懂:
find . -type f -ls
- 只要可以,請使用 xargs 或 parallel。注意,你可以控制每行(-L)執行多少個專案,以及並行執行(-P)。如果你不確定它是否會做正確的事情,可以首先使用 xargs echo。同時,使用 -I{} 也很方便。樣例:
find . -name '*.py' | xargs grep some_function cat hosts | xargs -I{} ssh root@{} hostname
- 比如說,你有一個文字檔案,如 Web 伺服器的日誌,在某些行中出現了某個特定的值,如 URL 中出現的 acct_id 引數。如果你想要統計有多少個 acct_id 的請求:
cat access.log | egrep -o 'acct_id=[0-9]+' | cut -d= -f2 | sort | uniq -c | sort -rn
- 執行該函式來獲得來自本文的隨機提示(解析Markdown並從中提取某個專案):
function taocl() { curl -s https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/README.md | pandoc -f markdown -t html | xmlstarlet fo --html --dropdtd | xmlstarlet sel -t -v "(html/body/ul/li[count(p)>0])[$RANDOM mod last()+1]" | xmlstarlet unesc | fmt -80 }
晦澀難懂,但卻有用
- expr:實施算術或布林操作,或者求正規表示式的值
- m4:簡單的巨集處理器
- yes:大量列印一個字串
- cal:漂亮的日曆
- env:(以特定的環境變數設定)執行一個命令(指令碼中很有用)
- look:查詢以某個字串開頭的英文單詞(或檔案中的行)
- cut 和 paste 以及 join:資料處理
- fmt:格式化文字段落
- pr:格式化文字為頁/列
- fold:文字折行
- column:格式化文字為列或表
- expand 和 unexpand:在製表符和空格間轉換
- nl:新增行號
- seq:列印數字
- bc:計算器
- factor:分解質因子
- gpg:加密併為檔案簽名
- toe:terminfo 條目表
- nc:網路除錯和資料傳輸
- socat:套接字中繼和 tcp 埠轉發(類似 netcat)
- slurm:網路流量視覺化
- dd:在檔案或裝置間移動資料
- file:識別檔案型別
- tree:以樹形顯示目錄及子目錄;類似 ls,但是是遞迴的。
- stat:檔案資訊
- tac:逆序列印檔案
- shuf:從檔案中隨機選擇行
- comm:逐行對比分類排序的檔案
- hd和bvi:轉儲或編輯二進位制檔案
- strings:從二進位制檔案提取文字
- tr:字元轉譯或處理
- iconv或uconv:文字編碼轉換
- split和csplit:分割檔案
- units:單位轉換和計算;將每雙週(fortnigh)一浪(浪,furlong,長度單位,約201米)轉換為每瞬(blink)一緹(緹,twip,一種和螢幕無關的長度單位)(參見: /usr/share/units/definitions.units)(LCTT 譯註:這都是神馬單位啊!)
- 7z:高比率檔案壓縮
- ldd:動態庫資訊
- nm:目標檔案的符號
- ab:Web 伺服器基準測試
- strace:系統呼叫除錯
- mtr:用於網路除錯的更好的路由追蹤軟體
- cssh:視覺化併發 shell
- rsync:通過 SSH 同步檔案和資料夾
- wireshark 和 tshark:抓包和網路除錯
- ngrep:從網路層摘取資訊
- host 和 dig:DNS查詢
- lsof:處理檔案描述符和套接字資訊
- dstat:有用的系統統計資料
- glances:高階,多個子系統概覽
- iostat:CPU和磁碟使用率統計
- htop:top的改進版
- last:登入歷史
- w:誰登入進來了
- id:使用者/組身份資訊
- sar:歷史系統統計資料
- iftop或nethogs:按套介面或程式的網路使用率
- ss:套介面統計資料
- dmesg:啟動和系統錯誤資訊
- hdparm:SATA/ATA 磁碟操作/改善效能
- lsb_release:Linux 發行版資訊
- lsblk:列出塊裝置,以樹形展示你的磁碟和分割槽
- lshw:硬體資訊
- fortune,ddate 和 sl:嗯,好吧,它取決於你是否認為蒸汽機車和 Zippy 引用“有用”
更多資源
- 超棒的shell: 一個shell工具和資源一覽表。
- 嚴格模式 用於寫出更佳的shell指令碼。
免責宣告
除了非常小的任務外,其它都寫出了程式碼供大家閱讀。伴隨力量而來的是責任。事實是,你能在Bash中做的,並不意味著是你所應該做的!;)
相關文章
- 命令列的藝術 (GitHub 星標 6 萬多)命令列Github
- 這篇文章可以幫你掌握命令列的藝術命令列
- 命令查詢分離的藝術
- 分享:Linux 命令列的藝術--走過路過不要錯過(37k 的 star)Linux命令列
- 加密的藝術加密
- 整合命令提示符、“多才多藝”的命令列檔案管理器 - CLEX命令列
- 技術選型的藝術
- 授權的藝術
- Linux Kernel程式碼藝術——陣列初始化Linux陣列
- Java垃圾收集的藝術Java
- 分析:操縱的藝術
- 設計的藝術(二):遊戲與遊戲性與互動藝術遊戲
- Linux 命令列黑技術(LTS)Linux命令列
- 視覺爆炸的藝術 | 《地平線:黎明時分》藝術賞析視覺
- 機器學習中資料清洗的藝術機器學習
- 有趣的CSS畫素藝術CSS
- 抽象的藝術-狀態機抽象
- 平衡-工作和生活的藝術
- 語言設計的藝術
- NFT生態藝術開發丨NFT數藏藝術丨NFT生態藝術系統開發技術
- 營銷區塊鏈技術的藝術區塊鏈
- 學習ASM技術(六)-- ASMCMD命令列ASM命令列
- JavaScript陣列的十八般武藝JavaScript陣列
- 軟體藝術 (轉)
- Redux中的程式設計藝術Redux程式設計
- 資料視覺化的藝術視覺化
- 第二課:資料的藝術
- 彪悍晨讀-清醒思考的藝術
- 優秀程式設計的“藝術”程式設計
- 組合語言的藝術(轉)組合語言
- 頗具藝術感的程式碼
- 多專案管理的藝術薦專案管理
- 主頁的藝術處理 (轉)
- Vue 3 Teleport:掌控渲染的藝術Vue
- TGDC | 探索人臉藝術背後的技術
- Windows XP中的命令列命令Windows命令列
- 晦澀難懂皆為藝術 第九藝術何時成了遊戲的擋箭牌?遊戲
- cmd 命令列 命令命令列