這篇文章可以幫你掌握命令列的藝術

巽離發表於2015-09-05

熟練地操作命令列是一項常常被我們忽視的技能,又或者說我們將它看的太過神祕。不過作為一名軟體工程師,掌握這一技能可以很大程度上提升我們工作的靈活性,提高工作效率。這篇文章是我在與 Linux 打交道的過程中總結出的一些小技巧。有些很基礎,有些也相當專業,不太好懂。這邊文章不長,不過如果你能在工作中充分使用這裡介紹的技能的話,那你也知之甚多了。

這裡的許多內容一開始已出現在 Quora(《每個Linux使用者都應該知道的命令列技巧》),但考慮到 Github 的使用者性質,他們比我有天賦而且可以隨時提出改進意見,因此使用 Github 更合適。如果你在本文中發現了錯誤或者存在可以改進的地方,請果斷提交 Issue 或 Pull Request!(當然在提交前請看一下必讀節和已有的 PR/issue)。

必讀

  • 這篇文件對新手與專家兩相宜。我們的目標是覆蓋面廣(儘量包括一切重要的內容),具體(給出最常見的具體的例子)以及簡潔(避免一些不必要以及不相干的東西)。 這裡介紹的小技巧可能在某個特定情境下至關重要,又或者能夠顯著地節約時間。
  • 本文為 Linux 所寫,除了“僅限 MacOS 系統”一節。其它節中的大部分內容都適用於其它 Unix 系統或 MacOS 系統,甚至 Cygwin。
  • 雖然我們介紹大多數技巧對其他 shell 以及 Bash 指令碼同樣管用,但是本文的關注點是:互動式 Bash。
  • 這裡涵蓋了“標準” Unix 命令以及其他需要安裝指定軟體包的命令——只要這個命令足夠重要,足夠管用,我就會在這裡提一提。

編注:為了控制篇幅,有些內容包含在引用裡面。你也可以通過 google 之類的工具來搜尋詳細資訊。使用 apt-get/yum/dnf/pacman/pip/brew 等命令來安裝新程式。

使用 Explainshell 來獲取命令、選項、管道等相關資訊的幫助。

基礎

學習基礎的bash用法,具體地說,閱讀bash的man手冊(man bash 並通讀一遍);很簡單而且不長。其他的shell也同樣可以,不過bash 是最通用的(如果只學習 zsh, fish 等,可能在你自己的工作環境中會用的很順暢,但是換個場景就歇菜了,比如在伺服器上操作)。

至少學習一種文字編輯器。最好是 vim (vi), 在終端隨機編輯文件方面,其他編輯器跟它相比沒有一點競爭力(即使大部分時間你用的是其他一些大型 IDE,比如 Emacs,或者一些其他時髦的編輯器)。如果你是 Vim 新手,推薦閱讀《Vim入門教程》、《25個Vim教程、視訊和資源》。

知道怎樣通過 man 來閱讀文件(好奇心重的人可能就直接 man man了,列出不同章節號,如:1表示普通的shell命令, 5表示檔案格式和規範, 8 代表系統管理命令等)。學會使用 apropos 來查詢 man 手冊。要知道有些命令不是可執行檔案,而是 bash 內建的,對於這種命令呢,可以使用 help 或者 help -d 來獲取幫助(例如 cd)。

學會使用 > 和 <  來重定向輸入輸出,學會使用 | 來建立管道。瞭解 > 用於覆蓋輸出檔案, >> 使用者追加到輸出檔案。 學習標準輸出 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 -hk *)。對檔案系統來說, df, mount, fdisk, mkfs,lsblk。

基本的網路管理命令: ip 或者 ifconfig, dig。

熟練掌握正規表示式,以及grep/egrep 工具的多種標誌。有必要知道 -i, -o, -A 以及 -B 選項的意思。

學會使用 apt-get, dnf 或者 pacman(根據不同的發行版選擇)來查詢或者安裝軟體包。確保你安裝了 pip 來安裝 python 相關的命令列工具。(下面介紹的那些工具都可以使用 pip 來安裝)

日常使用

在 Bash中,使用 Tab 鍵來補全命令,使用 ctrl-r 來查詢歷史命令。

在 Bash 中,使用 ctrl-w 來刪除上一個單詞,ctrl-u 刪除整行命令。使用 alt-b 和 alt-f 來逐單詞向前向後跳轉,ctrl-k 將滑鼠位置到行末的所有字元刪除,ctrl-l清屏。檢視 man readline 中的”Key Bindings”這一節瞭解 Bash 中預設的組合鍵。還有其他的很多,比如說 alt-. 可以用來上翻之前的命令,alt-* 擴充套件為當前目錄下的所有檔案。

如果你偏好 vi風格的組合鍵,可以 set -o vi。

使用history命令檢視近期的命令。還有其他許多簡寫命令,比如 !$表示上一個引數,!!執行上一條命令等。不過通常我們更常用的是 ctrl-r和alt-. 。

回到上一個工作目錄: cd –

如果你命令敲到一半發現還有其他事沒做,想要稍後執行,怎麼辦呢?使用 alt-# 給這條命令列首加上#,再回車當做一條註釋(或者使用 ctrl-a,#,回車)。之後通過歷史命令找它回來繼續往下敲。

Xargs 或者 parallel 命令也很管用。我們還可以使用 -L 或者 -P 選項限制每行引數個數。如果對執行結果不確定的話,可以先用 xargs echo 檢視。同樣, -I{} 選項用起來也很順手。例如:

【補充 : find . -name “.dsc” | xargs -L 2 echo 可以將當前目錄下所有 .dsc 檔案列出,並且限制每行顯示兩項。

find . -name “.dsc” | xargs -I{} mv {} {}.bak 可以將當前目錄下所有字尾為 .dsc 的檔案更名為 .dsc.bak

-I 選項告知 xargs 用每項的名稱替換 {}】

Pstree -p 可以很清晰的展示程式樹。

使用 pgrep 和 pkill 來根據名稱找出程式或者向程式傳送訊號 (注意 -f 的用法)

瞭解一些傳送給程式的訊號。比如,可以使用 kill -STOP [pid] 來停止程式。 Man 7 signal 檢視全部列表。

使用 nohup 或者 disown 命令讓程式在後臺一直執行。

通過 netstat -lntp 或者 ss -plat 命令來檢查哪些程式正在監聽埠(預設情況下監聽 TCP 埠, 使用 -u 監聽 UDP)。

使用 lsof 檢視所有開啟的套接字和檔案。詳情自 man

使用 alias 給常用命令建立別名。例如 alias ll=’ls -latr’ 為我們建立新的別名 ll。

在 Bash 指令碼中,使用 set -x 來除錯輸出。儘可能的使用嚴格模式。如果希望阻止我們的程式發生錯誤的情況下還繼續執行,可以通過設定 set -e 來限制。還可以使用 set -o pipefail 來嚴格限制錯誤(話說這種問題比較微妙,需要多領悟)。對於比較複雜而牽扯甚多的指令碼,可以使用 trap 。

在 Bash 指令碼中,子 shell (用括號包含)可以用來很方便的組織命令。一個常見用法是臨時切換到不同的工作目錄,例如:

請注意,在 Bash 中有多種變數展開的方式。

檢查某變數是否存在: ${name:?error message}。例如:如果某個 Bash 指令碼需要一個引數, input_file=${1:?usage: $0 input_file} 就可以。

數學展開式: i=$(((i+1)%5))

序列: {1..10}

截斷字串: ${var%suffix} 和 ${var#prefix},例如 var=foo.pdf, 命令 echo ${var%.pdf}.txt 列印 foo.txt。

通過 <(some command) 這種方式可以將命令的輸出視為檔案。例如,比較本地和遠端的 /etc/hosts 檔案:

要知道 Bash 中 “here documents” 的用法,比如 cat <<EOF …【man bash,搜尋 Here Documents】

在 Bash 中,通過 : some-command > logfile 2>&1 的方式來重定向標準輸出和標準錯誤。通常的,為了保證你執行的命令不會在標準輸入中殘留一個開啟的檔案控制程式碼,導致無法操作終端,最佳實踐是加上 </dev/null.

使用 man ascii 檢視十六進位制和十進位制值的ASCII表。man unicode,man utf-8,以及 man latin1 有助於你去了解通用的編碼資訊。

使用 screen 或者 tmux 命令來操作多屏,尤其是在連線遠端 session 、斷開或者重連 session 等情況下非常實用。另一個輕量級的儲存會話的工具是 dtach。

ssh 中,瞭解如何使用 -L 或 -D(偶爾需要用 -R)去開啟隧道是非常有用的,例如當你需要從一臺遠端伺服器上訪問 web。

優化 ssh 配置有時候可能很管用,例如下面這個 ~/.ssh/config 修改了一些配置,相對於使用預設配置的其他伺服器來說,它可以有效避免特定網路環境下連線被丟棄,使用壓縮資料(有效用於低頻寬連線中的 scp 操作),以及多通道等:

SSH 還有其他一些安全相關的選項,須小心使用,例如在單個子網、主機或者可信任的網路中:

獲取檔案的八進位制格式的許可權,這種許可權在系統配置中很管用,但是 ls 並不顯示,並且很容易搞砸。可以使用這條命令:

從另外一條命令中,以互動的方式選擇值,可以使用 percol 或者 fzf。

使用 fpp 來與其他命令輸出的檔案互動(如git)【facebook PathPicker, github 上的專案,例如 git status | fpp, find . -name “*.vala” | fpp】

對一個簡單的 web 伺服器來說,將當前目錄下所有的目錄(包括子目錄)展示給所處網路的所有使用者,使用: python -m SimpleHTTPServer 7777 (使用埠 7777 和 Python 2)或python -m http.server 7777 (使用埠 7777 和 Python 3)。

以某種許可權來執行命令,使用sudo(root 許可權)或sudo -u(其他使用者)。使用su或者sudo bash來啟動一個指定使用者許可權執行的 shell。使用su -模擬其他使用者的登入。

檔案和資料處理

在當前目錄下找到某名稱的檔案, find . -iname ‘*something’ (或其他類似方式)。找到其他任意位置的某個檔案,使用 locate something (但請注意: updatedb 可能無法索引到新增的檔案)

在原始碼或資料檔案中搜尋,使用 ag(比 grep -r 更好)。

將 HTML 檔案轉化為文字格式: lynx -dump -stdin

可以試試 pandoc 來對 Markdown、HTML 以及其他各種檔案進行格式轉換。

如果某些情況下你需要處理 XML 資料,那麼試試 xmlstarlet 吧,雖然它有點歷史滄桑感但的確挺好用的。

對 JSON 資料來說, 用 jq。

對於 Excel 或者 CSV 檔案, csvkit 提供諸如 in2csv, csvcut, csvjoin, csvgrep 等實用小工具。

關於 Amazon S3, s3cmd 很方便而 s4cmd 更快。 Amazon 官方的 aws 工具是其他 AWS 相關工作的基石。

瞭解如何使用 sort 和 uniq,包括 uniq 的 -u 引數和 -d 引數,詳見後文“一行命令”節。另外可以瞭解一下 comm。

瞭解如何使用 cut,paste 和 join 來更改檔案。很多人都會使用 cut,但幾乎都不會使用 join。

瞭解如何運用 wc 去計算新行數(-l),字元數(-m),單詞數(-w)以及位元組數(-c)。

知道用 tee 來將標準輸入的內容複製到檔案或者標準輸出,就像 ls -al | tee file.txt

要知道語言環境可能對許多命令列工具產生微妙地影響,包括排序的順序和效能。大多數 Linux 的安裝過程會將 LANG 或其他有關的變數設定本地化。但是請注意當你改變語言環境後,排序的結果可能會隨之變化。而且國際化可能會大大降低 sort 或其他命令的執行效率。某些情況下(例如集合運算、去重操作等)你可以放心的使用 export LC_ALL=C 來忽略掉國際化並使用基於位元組的順序。

瞭解 awk 和 sed 關於資料的簡單處理的用法。例如,將文字檔案中第三列的所有數字求和:

這可比 Python 實現的程式碼量少三倍也快三倍。

替換一個或多個檔案中出現的字串:

使用 rename 完成批量檔案的重新命名。對於更復雜的情況,可以使用 repren[https://github.com/jlevy/repren]

使用 shuf 從一個檔案中隨機選取行。

瞭解 sort 的引數。處理數字方面,使用 -n 或者 -h 來處理可讀性數字(例如 du -h 的輸出)。明白關鍵字的工作原理(-t 和 -k)。請注意!如果你想要僅按第一個域來排序需要 -k1,1 ; -k1 意味著按整行排序。穩定排序(sort -s)在某些情況下很有用。例如,以第二個域為主關鍵字,第一個域為次關鍵字進行排序,你可以使用 sort -k1,1 | sort -s -k2,2。

如果你想在 Bash 命令列中寫 tab 製表符(舉個例子: sort 的 -t 引數指定分隔符:sort -t”tab ” -k2 sortfile),按下 ctrl-v [Tab] 或鍵入 $’t’ (後者可能更好,因為你可以複製貼上它)。

標準的原始碼對比及合併工具是 diff 和 patch。使用 diffstat 檢視變更總覽資料。注意到 diff -r 對整個資料夾有效。使用 diff -r tree1 tree2 | diffstat 檢視變更總覽資料。

對於二進位制檔案,使用 hd 使其以十六進位制顯示以及使用 bvi 來編輯二進位制。

同樣對於二進位制檔案,使用 strings(加上 grep 等等)允許你查詢一些文字。

二進位制檔案對比(Delta 壓縮),使用 xdelta3。

使用 iconv 更改文字編碼。而更高階的用法,可以使用 uconv,它支援一些高階的 Unicode 功能。例如,這條命令將所有母音字母轉為小寫並移除了:

拆分檔案,檢視 split(按大小拆分)和 csplit(按模式拆分)。

使用 zless,zmore,zcat 和 zgrep對壓縮過的檔案進行操作。

系統除錯

對於 web 除錯來說,curl 和 curl -I 都是很趁手的工具,它們的好基友 wget 也不錯,或者是更時尚一點的 httpie。

使用 iostat、netstat、top (htop 更佳)和 dstat 去獲取硬碟、cpu 和網路的狀態。熟練掌握這些工具可以使你快速的對系統的當前狀態有一個大概瞭解。

若要對系統有一個深度的總體認識,使用 glances。它在一個終端視窗中向你提供一些系統級的資料。這對於快速的檢查各個子系統非常有幫助。

若要了解記憶體狀態,執行並理解 free 和 vmstat 的輸出。尤其注意“cached”的值,它指的是 Linux 核心用來作為檔案快取的記憶體大小,因此它與空閒記憶體無關。

Java 系統除錯則是另一碼事了,不過有一個簡單的小技巧可以用於 Oracle 的 JVM 或其他 JVM ,執行 kill -3 <pid>  會將一個完整的棧軌跡和堆概述(包括 GC 的細節)儲存到標準輸出/日誌檔案。

使用 mtr 去跟蹤路由,用於確定網路問題。

用 ncdu 來檢視磁碟使用情況,它比常用的命令,如 du -sh *,更節省時間。

查詢正在使用頻寬的套接字連線或程式,使用 iftop 或 nethogs。

ab 工具(捆綁於 Apache)可以簡單粗暴地檢查 web 伺服器的效能。對於更復雜的負載測試,使用 siege。

wireshark,tshark 和 ngrep 可用於複雜的網路除錯。

瞭解 strace 和 ltrace。當你想知道程式執行失敗、掛起甚至崩潰的原因,或者你想對效能有個總體瞭解的話,這兩個工具十分管用。注意 profile 引數(-c)和附加到一個執行的程式引數 (-p)。

瞭解 ldd 命令來檢查共享庫等等。

知道如何用 gdb 來除錯執行程式並獲取堆疊軌跡。

學會使用 /proc。它在除錯正在出現的問題的時候有時會效果驚人。比如:/proc/cpuinfo,/proc/meminfo,/proc/cmdline,/proc/xxx/cwd,/proc/xxx/exe,/proc/xxx/fd/,/proc/xxx/smaps。

如果想除錯已經發生的問題,sar 顯得很管用。它會列出CPU、記憶體、網路等歷史統計資料。

關於更深層次的系統分析以及效能分析,看看 stap(SystemTap),perf,以及sysdig。

檢視你當前使用的系統,使用 uname 或者 uname -a (Unix/kernel 資訊) or lsb_release -a (Linux 發行版資訊)。

如果某些問題看起來稍顯搞笑,試試檢視 dmesg 資訊(可能是硬體或驅動問題)。

一行命令

一些組合命令:

當你需要對文字檔案做集合交、並、差運算時, sort/uniq 聯合出擊顯得非常管用。假設 a 與 b 是兩個內容不同且去重的檔案。這種方式效率很高,並且對各種大小的檔案都適用,不管是在小檔案還是上G的大檔案。(sort 不受記憶體約束,不過如果 /tmp 所處的根分割槽容量有限,你可能需要 -T 引數),參閱前文中關於 LC_ALL 和 sort 的 -u 引數的部分。

使用 grep . * 命令來檢查目錄下所有檔案的內容,例如那些包含許多配置設定的目錄: /sys/, /proc/, /etc。

對文字檔案中第三列資料計算總和(相比python 快三倍,程式碼量卻只有 python 的1/3):

如果想檢視目錄樹中檔案的大小或者日期,下面這條命令類似遞迴的 ls -l,但是輸出結果比 ls -lR 更易讀:

Xargs 或者 parallel 命令也很管用。我們還可以使用 -L 或者 -P 選項限制每行引數個數。如果對執行結果不確定的話,可以先用 xargs echo 檢視。同樣, -I{} 選項用起來也很順手。例如:【前面已經有了,內容重複】

假設你有一個類似於 web 伺服器日誌檔案的文字檔案,並且某個特定值只會出現在某些行上,比如會在 URL 中出現的 acct_id 引數。如果你想計算出每個 acct_id 值有多少次請求,使用如下程式碼:

執行這個函式從這篇文件中隨機獲取一條小技巧(解析 Markdown 檔案並抽取專案):

管用的小冷門

expr:計算表示式、布林操作或正則匹配

m4:簡單地巨集處理器

yes:多次列印字串

cal:日曆

env:執行一個命令(指令碼檔案中很有用)

printenv: 列印環境變數(在除錯時或者指令碼中很管用)

look: 找出以某字串開頭的英文單詞(或者檔案中的某一行)

cut, paste 和 join :資料處理

fmt: 格式化文字段落

pr : 將文字格式化為頁資料或者列資料

fold : 封裝文字中的行【比如 -w 指定寬度,不使用預設的80】

Column: 將文字格式化為列或者表資料

expand 和 unexpand:製表符與空格之間轉換

nl:新增行號

seq:列印序列數字

bc:計算器

factor:分解因數 【例如 factor 100,輸出 2 2 5 5】

gpg:加密並簽名檔案

toe:終端型別列表

nc:網路除錯及資料傳輸

socat:套接字代理,與 netcat 類似

slurm:網路負載監視器

dd:在檔案或裝置間傳輸資料

file:確定檔案型別

tree:以樹的形式顯示路徑和檔案,類似於 ls,不過這條命令會遞迴顯示

stat:檔案資訊

time:執行命令,並計算執行時間

tac:反向輸出檔案

Shuf :將檔案中的資料隨機選擇排列

comm: 逐行比較已排序的檔案

pv: 監控通過管道的資料

hd 和 bvi:儲存或者編輯二進位制檔案

strings: 提取二進位制檔案的文字內容

Tr: 字元轉換與處理

Iconv 或 uconv: 文字編碼的轉換

Spit 和scplit : 分割檔案

Sponge: 在寫之前讀取所有輸入,在對同一個檔案讀寫很管用,例如: grep -v something some-file | sponge some-file 【將檔案中所有匹配 something 的行都刪除】

units:單位轉化與計算;將一種計量單位轉換為另一種等效的計量單位(參閱 /usr/share/units/definitions.units)

7z: 一種高效的壓縮工具

Ldd: 檢視動態庫的資訊

Nm: 提取可執行檔案或者 obj 檔案的符號

Ab:web 伺服器效能分析工具

Strace: 除錯系統呼叫

Mtr:網路除錯跟蹤工具

Cssh: 視覺化的併發 shell

Rsync :可用於遠端檔案目錄同步

Wireshark 和 tshark : 抓取包與網路除錯

Ngrep: 網路層的 grep 工具

Host 和 dig: DNS 查詢

Lsof : 處理檔案描述符和 socket 資訊【列出所有開啟的檔案】

dstat : 通用的系統統計工具

glances:高層次的多子系統概覽

iostat:CPU 和硬碟使用狀態

htop:top 的加強版

last:登入歷史記錄

w:當前登陸使用者

id:使用者/組 ID 資訊

sar: 系統歷史資料統計

iftop 或 nethogs:套接字及程式的網路利用率

ss:套接字資料統計

dmesg:引導及系統錯誤資訊

hdparm:SATA/ATA 磁碟操作及效能分析

lsb_release:Linux 發行版資訊

lsblk:列出塊裝置資訊:樹狀圖展示你的磁碟以及磁碟分割槽資訊

lshw,lscpu,lspci,lsusb 和 dmidecode:檢視硬體資訊,包括 CPU、BIOS、RAID、顯示卡、其他裝置等

fortune,ddate 和 sl:開個玩笑…… 如果對心靈雞湯或者奔跑的小火車感興趣的話,可以自己試試

僅限 MacOS

以下是僅限於 MacOS 系統的技巧

  • 用 brew (Homebrew)或者 port (MacPorts)進行包管理。這些可以用來在 Mac 系統上安裝以上的大多數命令。
  • 用 pbcopy 複製任何命令的輸出到桌面應用,用 pbpaste 貼上輸入。
  • 用 open 或者 open -a /Applications/Whatever.app 使用桌面應用開啟檔案。
  • Spotlight: 用 mdfind 搜尋檔案,用 mdls 列出後設資料(例如照片的 EXIF 資訊)。

注意 MacOS 系統是基於 BSD UNIX 的,許多命令(例如 ps,ls,tail,awk,sed)都和 Linux 中有些微的不同,受 System V-style Unix 和 GNU 工具影響很大。你可以通過標題為 “BSD General Commands Manual” 的 man 頁面發現這些不同。在有些情況下 GNU 版本的命令也可能被安裝(例如 gawk 和 gsed 對應 GNU 中的 awk 和 sed )。如果要寫跨平臺的 Bash 指令碼,避免使用這些命令(例如,考慮 Python 或者 perl )或者經過仔細的測試。

更多資源

免責宣告

除了特別不起眼的功能外,為了方便大家閱讀,這裡寫了一些 shell 指令碼程式碼。但是伴隨著力量而來的是責任。總之,執行需謹慎。

相關文章