揭開UNIX高手的那些重大秘密
Pixel, Byte, and Comma的軟件開發(fā)者M(jìn)artin Streicher 在本文中為我們揭示了UNIX高手的秘密。Martin Streicher 是一位 Ruby on Rails 的自由開發(fā)人員和 Linux Magazine 的前任主編。Martin 畢業(yè)于 Purdue University 并獲得計(jì)算機(jī)科學(xué)學(xué)位,從 1986 年起他一直從事 UNIX 類系統(tǒng)的編程工作。他喜歡收集藝術(shù)品和玩具。
保存環(huán)境變量
大多數(shù) UNIX 用戶在 .bashrc(針對(duì) Bash shell)和 .zshrc(針對(duì) Z shell)等 shell 啟動(dòng)文件中塞滿大量用戶設(shè)置,以便一次又一次地重建鐘愛的 shell 環(huán)境。啟動(dòng)文件能夠創(chuàng)建別名、設(shè)置 shell 選項(xiàng)、創(chuàng)建函數(shù)、以及設(shè)置環(huán)境變量。關(guān)鍵的環(huán)境變量包括 HOME(指向您的主目錄)、PATH(列舉從中搜索應(yīng)用程序的目錄)和 MANPATH(列舉從中搜索手冊(cè)頁的目錄)。要查看您的 shell 中設(shè)置了哪些環(huán)境變量,鍵入 printenv
命令。查閱 shell 手冊(cè)頁,獲取可用環(huán)境變量的完整列表。
與 shell 一樣,可以通過環(huán)境變量定制其他許多 UNIX 應(yīng)用程序。例如,Java 子系統(tǒng)要求定義 JAVA_HOME 來指向 Java 運(yùn)行時(shí)的根。同樣,Amazon Web Services (AWS) 實(shí)用程序套件強(qiáng)制使用 AWS_CREDENTIAL_FILE 來指向一個(gè)包含有效私匙憑證的文件。單獨(dú)的應(yīng)用程序也提供環(huán)境變量,關(guān)鍵是如何發(fā)現(xiàn)這些變量。幸運(yùn)的是,這種工作不需要非法入侵;相反,只需查詢手邊的實(shí)用工具手冊(cè)頁,查找標(biāo)題為 “Environment Variables” 的章節(jié)即可。
例如,分頁實(shí)用程序 less
定義了幾個(gè)有用的環(huán)境變量:
◆環(huán)境變量 LESS 存儲(chǔ)一些命令行選項(xiàng),以在您每次調(diào)用該分頁程序時(shí)減少鍵入量。例如,如果您需要閱讀大量日志文件,可將以下語句添加到一個(gè) shell 啟動(dòng)文件中:
export LESS='--RAW-CONTROL-CHARACTERS --squeeze-lines --ignore-case' |
上述選項(xiàng)將分別解譯控制字符(通常是語法著色),將多個(gè)空行壓縮為一行,并忽略字符串匹配中的大小寫。如果您使用代碼,可嘗試以下選項(xiàng):
export LESS='--LINE-NUMBERS --quit-if-one-screen --quit-on-intr' |
◆名為 LESSKEY 的環(huán)境設(shè)置指向一個(gè)密匙綁定文件??梢允褂妹艹捉壎▉矶ㄖ?less
的行為,比如,匹配另一個(gè)頁面或編輯器的行為。
◆與 shell 一樣,less
能保留多個(gè)調(diào)用之間的歷史。設(shè)置 LESSHISTFILE 和 LESSHISTSIZE 分別指向一個(gè)持久命令文件和設(shè)置要記錄的命令的最大條數(shù)。
GNU Compiler Collection (GCC) 是另一個(gè)典型的環(huán)境變量應(yīng)用示例。GCC 定義各種環(huán)境變量來定制其操作。LIBRARY_PATH,顧名思義,是一個(gè)目錄列表,用于搜索要鏈接到的庫;COMPILER_PATH 的工作方式與 shell 的 PATH 非常相似,但是由 GCC 在內(nèi)部使用,用于查找編譯過程中使用的子程序。
如果您針對(duì)單個(gè)平臺(tái)寫代碼并構(gòu)建二進(jìn)制文件,您可能永遠(yuǎn)也不會(huì)用到這些環(huán)境變量,但是,如果您跨平臺(tái)交叉編譯相同的代碼,那么這些變量對(duì)于訪問每個(gè)平臺(tái)的不同的頭部和庫至關(guān)重要。您可以將這些變量設(shè)置為不同的值集合,一個(gè)集合針對(duì)一種機(jī)器,而另一個(gè)集合針對(duì)另一種風(fēng)格的系統(tǒng)。
事實(shí)上,您可以從 GCC 獲得一個(gè)暗示:可以為每個(gè)應(yīng)用程序維護(hù)多個(gè)環(huán)境變量集合,根據(jù)手邊的工作從一個(gè)集合切換到另一個(gè)集合。一種方法是在每個(gè)項(xiàng)目目錄中保存一個(gè)環(huán)境初始化文件并根據(jù)需要 source
它。例如,許多 Ruby 開發(fā)人員使用這種方法來在不同的 Ruby 版本間切換,根據(jù)需要更改環(huán)境變量 PATH、GEM_HOME 和 GEM_PATH,從一個(gè)版本跳到另一個(gè)版本。
“點(diǎn)綴” 環(huán)境
與環(huán)境變量非常相似的是,許多 Linux和 UNIX 應(yīng)用程序都提供一個(gè)點(diǎn) 文件 — 文件名以圓點(diǎn)開始的小文件 — 來進(jìn)行定制。與環(huán)境變量不同的是:環(huán)境變量采集少量標(biāo)記和相對(duì)較少的信息量,而點(diǎn)文件可能更廣泛、更復(fù)雜,擁有自己獨(dú)特的語法規(guī)則、甚至自己的編程語言。點(diǎn)文件是保存選項(xiàng)和設(shè)置的理想位置,因?yàn)椋ǜ鶕?jù) UNIX 傳統(tǒng))以一個(gè)圓點(diǎn)開始的文件名不會(huì)出現(xiàn)在標(biāo)準(zhǔn)的目錄清單中。(使用 ls -a
來查看這些所謂的隱藏文件。)點(diǎn)文件是純文本文件,只是文件名比較特別而已。
點(diǎn)文件通常位于您的主目錄內(nèi),但有些實(shí)用程序也在當(dāng)前工作目錄中查找點(diǎn)文件。如果一個(gè)應(yīng)用程序支持多個(gè)點(diǎn)文件,則該程序通常應(yīng)用于優(yōu)先規(guī)則,來表明一個(gè)文件比另一個(gè)文件優(yōu)先。通常,“本地” 點(diǎn)文件 — 位于當(dāng)前工作目錄 — 優(yōu)先級(jí)最高,然后是主目錄中的點(diǎn)文件,最后是一個(gè)系統(tǒng)范圍配置文件。這些文件可以全部存在,也可以存在一個(gè),或者都不存在,這取決于應(yīng)用程序?qū)⑦@些文件視為互斥的還是遞增的。在第一種情況下,優(yōu)先鏈中第一個(gè)點(diǎn)文件的優(yōu)先權(quán)是不容置疑的。在后一種情況下,配置可以級(jí)聯(lián)或融解到最終結(jié)果中。
less
的密匙綁定文件是一個(gè)簡(jiǎn)單點(diǎn)文件示例,位于 $HOME/.lesskey 中。文件中的每一行都是一對(duì)(一個(gè)按鍵和一條命令),如下所示:
\r forw-line \n forw-line e forw-line j forw-line ^E forw-line ^N forw-line k back-line y back-line ^Y back-line |
fetchmail
是比較復(fù)雜的點(diǎn)文件示例。這個(gè)實(shí)用程序在本地從多個(gè)遠(yuǎn)程源提取電子郵件并傳送消息。這個(gè)實(shí)用程序的操作只通過 $HOME/.fetchmailrc 控制。(參見手冊(cè)頁了解它的眾多選項(xiàng)。)cron
、git
、vi
,以及其他許多命令都能識(shí)別點(diǎn)文件。同樣,請(qǐng)閱讀這個(gè)應(yīng)用程序的手冊(cè)頁,了解可以在點(diǎn)文件中配置的內(nèi)容。有些點(diǎn)文件內(nèi)容豐富,足以占用一個(gè)單獨(dú)的手冊(cè)頁,比如 crontab
。
#p#
噓......關(guān)于 SSH 的秘密
Secure Shell (SSH) 是一個(gè)功能強(qiáng)大的子系統(tǒng),用于安全地登錄到遠(yuǎn)程系統(tǒng)、復(fù)制文件并穿越防火墻。由于 SSH 是一個(gè)子系統(tǒng),它提供大量選項(xiàng)來定制和簡(jiǎn)化其操作。事實(shí)上,SSH 提供名為 $HOME/.ssh 的整個(gè) “點(diǎn)目錄” 來包含其所有數(shù)據(jù)。(您的 .ssh 目錄必須是模式 600,以阻止他人訪問。非 600 模式將干擾正常的操作。)特別是,文件 $HOME/.ssh/config 可以定義大量快捷方式,包括機(jī)器名稱的別名、每主機(jī)訪問控制等。
下面是位于 $HOME/.ssh/config 中的一個(gè)典型代碼塊,用于定制一個(gè)特定主機(jī)的 SSH:
Host worker HostName worker.example.com IdentityFile ~/.ssh/id_rsa_worker User joeuser |
~/.ssh/config 中的每個(gè)塊配置一個(gè)或多個(gè)主機(jī)。不同的塊使用一個(gè)空行分隔。這個(gè)塊使用 4 個(gè)選項(xiàng):Host
、HostName
、IdentityFile
和 User
。Host
為 HostName
指定的機(jī)器創(chuàng)建一個(gè)昵稱。昵稱允許您鍵入 ssh worker
,而不是 ssh worker.example.com
。另外,IdentityFile
和 User
選項(xiàng)指定如何登錄到 worker
。前者指向此主機(jī)使用的一個(gè)私匙,后者提供登錄 ID。這樣,這個(gè)代碼塊就等同于以下命令:
ssh joeuser@worker.example.com -i ~/.ssh/id_rsa_worker |
ControlMaster
是一個(gè)鮮為人知的強(qiáng)大選項(xiàng)。如果設(shè)置,同一個(gè)主機(jī)的多個(gè) SSH 會(huì)話將共享單個(gè)連接。一旦第一個(gè)連接建立,后續(xù)連接就不再需要憑證,從而消除了每次連接同一機(jī)器都需要鍵入密碼的麻煩。ControlMaster
非常方便,您可能愿意為每臺(tái)機(jī)器啟用它。啟用方法非常簡(jiǎn)單,只需使用主機(jī)通配符 *
:
Host * ControlMaster auto ControlPath ~/.ssh/master-%r@%h:%p |
如您所料,標(biāo)記了 Host *
的塊將應(yīng)用到每個(gè)主機(jī),甚至是那些在配置文件中沒有明確指定的主機(jī)。ControlMaster auto
嘗試使用一個(gè)現(xiàn)有連接,并在沒有發(fā)現(xiàn)共享連接時(shí)創(chuàng)建一個(gè)新連接。ControlPath
指向一個(gè)文件,以便持久化一個(gè)控制套接字以供共享。%r
用遠(yuǎn)程登錄用戶名替換,%h
用目標(biāo)主機(jī)名替換,%p
代替連接使用的端口。(您還可以使用 %l
,它使用本地主機(jī)名替換。)上述規(guī)范使用類似于下面的文件名創(chuàng)建控制套接字:
master-joeuser@worker.example.com:22 |
當(dāng)?shù)竭h(yuǎn)程主機(jī)的所有連接都被切斷時(shí),每個(gè)控制套接字都就會(huì)被移除。如果您想隨時(shí)了解連接到了哪些主機(jī),只需鍵入 ls ~/.ssh
并查看控制套接字的主機(jī)名部分(%h
)。
SSH 配置文件非常大,它也有自己的手冊(cè)頁。鍵入 man ssh_config
查看所有可能的選項(xiàng)。這里有一個(gè)巧妙的 SSH 技巧:可以通過 SSH 從本地系統(tǒng)進(jìn)入遠(yuǎn)程系統(tǒng)。要用到的命令行如下所示:
$ ssh example.com -L 5000:localhost:3306 |
這條命令的意思是:通過 example.com 進(jìn)行連接,并在本地機(jī)器上的端口 5000 和名為 “localhost” 的機(jī)器上的端口 3306(MySQL 服務(wù)器端口)之間建立一條通道。由于 localhost
在 example.com 上解釋(因?yàn)橥ǖ酪呀ⅲ?,因?localhost
就是 example.com。由于出站通道 — 以前稱為本地轉(zhuǎn)發(fā)(local forward)— 已建立,本地客戶端能夠連接到端口 5000,并與 example.com 上運(yùn)行的 MySQL 服務(wù)器通信。
通道創(chuàng)建的常規(guī)形式如下:
$ ssh proxyhost localport:targethost:targetport |
其中,proxyhost
是可以通過 SSH 訪問的機(jī)器,并且擁有一個(gè)到 targethost
的網(wǎng)絡(luò)連接(不通過 SSH)。localport
是您的本地系統(tǒng)上的一個(gè)非特權(quán)端口(1024 以上的任一未用端口),targetport
是您要連接到的服務(wù)的端口。
前面的命令從您的機(jī)器發(fā)送出去,到達(dá)外部世界。 也可以使用 SSH 發(fā)送進(jìn)來,或者從外部世界連接到您的本地系統(tǒng)。入站通道的常規(guī)形式如下:
$ ssh user@proxyhost -R proxyport:targethosttargetport |
建立一條入站通道 — 以前稱為遠(yuǎn)程轉(zhuǎn)發(fā) — 時(shí),proxyhost
和 targethost
的角色將被反轉(zhuǎn):目標(biāo)是您的本地機(jī)器,代理是遠(yuǎn)程機(jī)器。user
是您在代理上的登錄名。以下命令提供了一個(gè)具體示例:
$ ssh joe@example.com -R 8080:localhost:80 |
這條命令的意思是:用戶 Joe 連接到 example.com,并將遠(yuǎn)程端口連接到本地端口 80。這條命令向 example.com 上的用戶提供一個(gè)通道,以連接到 Joe 的機(jī)器上。遠(yuǎn)程用戶能夠連接到 8080,以便連接 Joe 機(jī)器上的 Web 服務(wù)器。
除了分別用于本地和遠(yuǎn)程轉(zhuǎn)發(fā)的 -L
和 -R
之外,SSH 還提供 -D
參數(shù)來在遠(yuǎn)程機(jī)器上創(chuàng)建一個(gè) HTTP 代理。請(qǐng)參見 SSH 手冊(cè)頁了解正確語法。
#p#
使用歷史記錄重寫
如果您經(jīng)常在 shell 提示符中花費(fèi)大量時(shí)間,保存 shell 歷史記錄可以節(jié)約時(shí)間和輸入。但是如果歷史記錄不能被修改,就會(huì)導(dǎo)致一些麻煩:記錄重復(fù)的命令,且多個(gè) shell 實(shí)例可能會(huì)干擾各自的歷史記錄。這兩個(gè)問題很容易解決,只需在您的 .bashrc 中添加兩行:
export HISTCONTROL=ignoreboth shopt -s histappend |
第一行將移除您的 shell 歷史記錄中連續(xù)的重復(fù)命令。如果您想移除所有零散的副本,可將 ignoreboth
更改為 erasedups
。第二行在 shell 退出時(shí)將 shell 的歷史記錄附加到您的記錄文件。默認(rèn)情況下,Bash 記錄文件命名為 ~/~/.bash_history (不錯(cuò),這是一個(gè)點(diǎn)文件)??梢酝ㄟ^設(shè)置 HISTFILE(不錯(cuò),這是一個(gè)環(huán)境變量)來更改它的位置。如果您想將一個(gè) shell 的最近 10,000 命令保存在一個(gè)包含 100,000 條目的記錄文件中,將 export HISTSIZE=10000 HISTFILESIZE=100000
添加到您的 shell 啟動(dòng)文件中。要查看一個(gè) shell 的歷史記錄,在任意提示處鍵入 history
即可。
如果不能調(diào)用,那么保存的命令歷史記錄就用處不大。而這正是 shell !
(或 bang)操作符的作用所在:
!!
("bang bang") 完整地重復(fù)最后一條命令。!:0
是前一條命令的名稱。!^
是前一條命令的第一個(gè)參數(shù)。!:2
、!:3
...!$
等命令是前一條命令的第二、第三......以及最后一個(gè)參數(shù)。!*
是最后一條命令的所有參數(shù),命令名除外。!n
重復(fù)歷史中編號(hào)為n
的命令。!handle
重復(fù)以handle
中的字符開始的最后一條命令。例如,!ca
將重復(fù)以字符ca
開始的最后一條命令,如cat README
。!?handle
重復(fù)包含handle
中的字符組成的字符串的最后一條命令。例如,!?READ
還會(huì)匹配cat README
。^original^substitution
使用substitution
替換original
的第一個(gè) 實(shí)例。例如,如果前一條命令是cat README
,,命令^README^license.txt
將生成一條新命令cat license.txt
。!:gs/original/substitution
將使用substitution
替換original
的所有 實(shí)例(!:gs
表示 “全局替換[global substitution]”)。!-2
是倒數(shù)第二條命令,!-3
是倒數(shù)第三條命令,以此類推。
您甚至可以合并歷史表達(dá)式來生成 !-2:0 -R !^ !-3:2
這樣的 “魔湯”,該命令將擴(kuò)展為倒數(shù)第二條命令的名稱,加上 -R
,再加上前一條命令的第一個(gè)參數(shù),以及倒數(shù)第三條命令的第二個(gè)參數(shù)。要使這樣的神秘命令更具可讀性,可以在鍵入時(shí)擴(kuò)展歷史參考。在任意提示符鍵入命令 bind Space:magic-space
,或者將其添加到一個(gè)啟動(dòng)文件,從而將空格鍵綁定到函數(shù) magic-space,該函數(shù)將擴(kuò)展內(nèi)聯(lián)歷史替換。
#p#
與擴(kuò)展名無關(guān)的自動(dòng)解壓
鑒于 Internet 上有如此眾多的代碼,您可能每天都會(huì)下載數(shù)十個(gè)文件。可能會(huì)出現(xiàn)這樣的情況:所有那些文件都使用不同的方式打包 — 有的是 ZIP 文件,有的是 RAR 文件,還有很多是 tarball 文件,盡管每個(gè)包都使用不同的實(shí)用程序壓縮。記住如何解壓縮和擴(kuò)展每種包格式將會(huì)使人精疲力盡。那么,為何不在單個(gè)命令中完成所有那些任務(wù)呢?下面這個(gè)函數(shù)在許多樣例點(diǎn)文件中廣泛可用:
ex () { if [ -f $1 ] ; then case $1 in *.tar.bz2) tar xjf $1 ;; *.tar.gz) tar xzf $1 ;; *.bz2) bunzip2 $1 ;; *.rar) rar x $1 ;; *.gz) gunzip $1 ;; *.tar) tar xf $1 ;; *.tbz2) tar xjf $1 ;; *.tgz) tar xzf $1 ;; *.zip) unzip $1 ;; *.Z) uncompress $1 ;; *.7z) 7z x $1 ;; *) echo "'$1' cannot be extracted via extract()" ;; esac else echo "'$1' is not a valid file" fi } |
這個(gè)函數(shù) ex
擴(kuò)展了 11 種文件格式;如果要處理其他包類型,該函數(shù)還可以擴(kuò)展。一旦定義 — 例如,在一個(gè) shell 啟動(dòng)文件中 — 就可以簡(jiǎn)單地鍵入 ex somefile
,其中 somefile
以以下一種已命名擴(kuò)展結(jié)束:
$ ls source $ tar czf source.tgz source $ ls -1 source source.tgz $ rm -rf source $ ex source.tgz $ ls -1 source source.tgz |
順便說一下,如果您將今天下載的文件放錯(cuò)了位置,可以運(yùn)行 find
來查找它:
$ find ~ -type f -mtime 0 |
命令 -type f
查找純文本文件,-mtime 0
查找自當(dāng)天午夜以來創(chuàng)建的文件。
更多秘密
需要揭開的專家秘密還有很多。在 Web 上搜索 “shell auto-complete”,進(jìn)一步了解自動(dòng)完成特性,該特性用于在您鍵入一條命令時(shí)提供上下文敏感的擴(kuò)展。另外,搜索 “shell prompts” 以了解如何定制您的 shell 提示:可以將其設(shè)置為彩色,可以設(shè)置您的當(dāng)前工作目錄或 Git 分支,還可以顯示歷史數(shù)目 — 如果經(jīng)常調(diào)用歷史,這是一個(gè)方便的參考信息。要查看工作示例,可在 Github 中搜索 “dot files”。許多專家都將他們的 shell 配置張貼在 Github 上。
【編輯推薦】