熟練地操作命令行是一項常常被我們忽視的技能,又或者說我們將它看的太過神秘。不過作為一名軟體工程師,掌握這一技能可以很大程度上提升我們工作的靈活性,提高工作效率。這篇文章是我在與 Linux 打交道的過程中總結出的一些小技巧。有些很基礎,有些也相當專業,不太好懂。這篇文章不長,不過如果你能在工作中充分使用這裡介紹的技能的話,那你也知之甚多了。
這裡的許多內容一開始已出現在 Quora,但考慮到 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,或者一些其他時髦的編輯器)。
知道怎樣通過 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 '*.py' | xargs grep some_function
cat hosts | xargs -I{} ssh root@{} hostname
【補充 : 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 (用括號包含)可以用來很方便的組織命令。一個常見用法是臨時切換到不同的工作目錄,例如:
# 在當前目錄工作
(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},例如 var=foo.pdf, 命令 echo ${var%.pdf}.txt 列印 foo.txt。
通過 <(some command) 這種方式可以將命令的輸出視為文件。例如,比較本地和遠程的 /etc/hosts 文件:
diff /etc/hosts <(ssh somehost cat /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 操作),以及多通道等:
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 或者 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 -模擬其他用戶的登錄。
文件和數據處理
若要了解內存狀態,運行並理解 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 參數的部分。
cat a b | sort | uniq > c # c is a union b
cat a b | sort | uniq -d > c # c is a intersect b
cat a b b | sort | uniq -u > c # c is set difference a - b
使用 grep . * 命令來檢查目錄下所有文件的內容,例如那些包含許多配置設置的目錄: /sys/, /proc/, /etc。
對文本文件中第三列數據計算總和(相比python 快三倍,代碼量卻只有 python 的1/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 值有多少次請求,使用如下代碼:
運行這個函數從這篇文檔中隨機獲取一條小技巧(解析 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:執行一個命令(腳本文件中很有用)
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 腳本代碼。但是伴隨著力量而來的是責任。總之,運行需謹慎。