使 Bash 工作的更好的技巧。
每個行業都有一個該行業的大師們最常使用的工具。 對於許多系統管理員來說,這個工具就是他們的 shell。 在大多數 Linux 和其他類 Unix 系統上,預設的 shell 是 Bash。
Bash 是一個相當古老的程式——它起源於 20 世紀 80 年代後期——但它建立在更多更老的 shell 上,比如 C shell(csh),csh 至少是它 10 年前的前輩了。 因為 shell 的概念是那麼古老,所以有大量的神祕知識等待著系統管理員去吸收領悟,使其生活更輕鬆。
我們來看看一些基礎知識。
在某些時候,誰曾經無意中以 root 身份執行命令並導致某種問題? 舉手
我很確定我們很多人一度都是那個人。 這很痛苦。 這裡有一些非常簡單的技巧可以防止你再次碰上這類問題。
使用別名
首先,為 mv
和 rm
等命令設定別名,指向 mv -i
和 rm -i
。 這將確保在執行 rm -f /boot
時至少需要你確認。 在 Red Hat 企業版 Linux 中,如果你使用 root 帳戶,則預設設定這些別名。
如果你還要為普通使用者帳戶設定這些別名,只需將這兩行放入家目錄下名為 .bashrc
的檔案中(這些也適用於 sudo
):
1 2 |
alias mv='mv -i' alias rm='rm -i' |
讓你的 root 提示符脫穎而出
你可以採取的防止意外發生的另一項措施是確保你很清楚在使用 root 帳戶。 在日常工作中,我通常會讓 root 提示符從日常使用的提示符中脫穎而出。
如果將以下內容放入 root 的家目錄中的 .bashrc
檔案中,你將看到一個黑色背景上的紅色的 root 提示符,清楚地表明你(或其他任何人)應該謹慎行事。
export PS1=”
\[$(tput bold)$(tput setab 0)$(tput setaf 1)\]
\u@\h:\w #\[$(tput sgr0)\]
”
實際上,你應該儘可能避免以 root 使用者身份登入,而是通過 sudo
執行大多數系統管理命令,但這是另一回事。
使用了一些小技巧用於防止使用 root 帳戶時的“不小心的副作用”之後,讓我們看看 Bash 可以幫助你在日常工作中做的一些好事。
控制你的歷史
你可能知道在 Bash 中你按向上的箭頭時能看見和重新使用你之前所有(好吧,大多數)的命令。這是因為這些命令已經儲存到了你家目錄下的名為 .bash_history
的檔案中。這個歷史檔案附帶了一組有用的設定和命令。
首先,你可以通過鍵入 history
來檢視整個最近的命令歷史記錄,或者你可以通過鍵入 history 30
將其限制為最近 30 個命令。不過這技巧太平淡無奇了(LCTT 譯註: vanilla 原為香草,後引申沒擴充的、標準、普通的,比如 vanilla C++ compiler 意為標準 C++ 編譯器)。 你可以更好地控制 Bash 儲存的內容以及儲存方式。
例如,如果將以下內容新增到 .bashrc
,那麼任何以空格開頭的命令都不會儲存到歷史記錄列表中:
1 |
HISTCONTROL=ignorespace |
如果你需要以明文形式將密碼傳遞給一個命令,這就非常有用。 (是的,這太可怕了,但它仍然會發生。)
如果你不希望經常執行的命令充斥在歷史記錄中,請使用:
1 |
HISTCONTROL=ignorespace:erasedups |
這樣,每次使用一個命令時,都會從歷史記錄檔案中刪除之前出現的所有相同命令,並且只將最後一次呼叫儲存到歷史記錄列表中。
我特別喜歡的歷史記錄設定是 HISTTIMEFORMAT
設定。 這將在歷史記錄檔案中在所有的條目前面新增上時間戳。 例如,我使用:
1 |
HISTTIMEFORMAT="%F %T " |
當我輸入 history 5
時,我得到了很好的完整資訊,如下所示:
1 2 3 4 5 |
1009 2018-06-11 22:34:38 cat /etc/hosts 1010 2018-06-11 22:34:40 echo $foo 1011 2018-06-11 22:34:42 echo $bar 1012 2018-06-11 22:34:44 ssh myhost 1013 2018-06-11 22:34:55 vim .bashrc |
這使我更容易瀏覽我的命令歷史記錄並找到我兩天前用來建立到我家實驗室的 SSH 連線(我一次又一次地忘記……)。
Bash 最佳實踐
我將在編寫 Bash 指令碼時最好的(或者至少是好的,我不要求無所不知)11 項實踐列出來。
11、 Bash 指令碼可能變得複雜,不過註釋也很方便。 如果你在考慮是否要新增註釋,那就新增一個註釋。 如果你在週末之後回來並且不得不花時間搞清楚你上週五想要做什麼,那你是忘了新增註釋。
10、 用花括號括起所有變數名,比如 ${myvariable}
。 養成這個習慣可以使用 ${variable}_suffix
這種用法了,還能提高整個指令碼的一致性。
9、 計算表示式時不要使用反引號;請改用 $()
語法。 所以使用:
1 |
for file in $(ls); do |
而不使用:
1 |
for file in `ls`; do |
前一個方式是可巢狀的,更易於閱讀的,還能讓一般的系統管理員群體感到滿意。 不要使用反引號。
8、 一致性是好的。 選擇一種風格並在整個指令碼中堅持下去。 顯然,我喜歡人們選擇 $()
語法而不是反引號,並將其變數包在花括號中。 我更喜歡人們使用兩個或四個空格而不是製表符來縮排,但即使你選擇了錯誤的方式,也要一貫地錯下去。
7、 為 Bash 指令碼使用適當的釋伴shebang(LCTT 譯註:Shebang,也稱為 Hashbang ,是一個由井號和歎號構成的字元序列 #!
,其出現在文字檔案的第一行的前兩個字元。 在檔案中存在釋伴的情況下,類 Unix 作業系統的程式載入器會分析釋伴後的內容,將這些內容作為直譯器指令,並呼叫該指令,並將載有釋伴的檔案路徑作為該直譯器的引數)。 因為我正在編寫Bash指令碼,只打算用 Bash 執行它們,所以我經常使用 #!/usr/bin/bash
作為我的釋伴。 不要使用 #!/bin/sh
或 #!/usr/bin/sh
。 你的指令碼會被執行,但它會以相容模式執行——可能會產生許多意外的副作用。 (當然,除非你想要相容模式。)
6、 比較字串時,在 if
語句中給變數加上引號是個好主意,因為如果你的變數是空的,Bash 會為這樣的行丟擲一個錯誤:
1 2 3 |
if [ ${myvar} == "foo" ]; then echo "bar" fi |
對於這樣的行,將判定為 false
:
1 2 3 |
if [ "${myvar}" == "foo" ]; then echo "bar" fi |
此外,如果你不確定變數的內容(例如,在解析使用者輸入時),請給變數加引號以防止解釋某些特殊字元,並確保該變數被視為單個單詞,即使它包含空格。
5、 我想這是一個品味問題,但我更喜歡使用雙等號( ==
),即使是比較 Bash 中的字串。 這是一致性的問題,儘管對於字串比較,只有一個等號會起作用,我的思維立即變為“單個 =
是一個賦值運算子!”
4、 使用適當的退出程式碼。 確保如果你的指令碼無法執行某些操作,則會向使用者顯示已寫好的失敗訊息(最好提供解決問題的方法)併傳送非零退出程式碼:
1 2 3 |
# we have failed echo "Process has failed to complete, you need to manually restart the whatchamacallit" exit 1 |
這樣可以更容易地以程式設計方式從另一個指令碼呼叫你的指令碼並驗證其成功完成。
3、 使用 Bash 的內建機制為變數提供合理的預設值,或者如果未定義你希望定義的變數,則丟擲錯誤:
1 2 |
# this sets the value of $myvar to redhat, and prints 'redhat' echo ${myvar:=redhat} |
1 2 |
# this throws an error reading 'The variable myvar is undefined, dear reader' if $myvar is undefined ${myvar:?The variable myvar is undefined, dear reader} |
2、 特別是如果你正在編寫大型指令碼,或者是如果你與其他人一起開發該大型指令碼,請考慮在函式內部定義變數時使用 local
關鍵字。 local
關鍵字將建立一個區域性變數,該變數只在該函式中可見。 這限制了變數衝突的可能性。
1、 每個系統管理員有時必須這樣做:在控制檯上除錯一些東西,可能是資料中心的真實伺服器,也可能是虛擬化平臺的虛擬伺服器。 如果你必須以這種方式除錯指令碼,你會感謝你自己記住了這個:不要讓你的指令碼中的行太長!
在許多系統上,控制檯的預設寬度仍為 80 個字元。 如果你需要在控制檯上除錯指令碼並且該指令碼有很長的行,那麼你將成為一個悲傷的熊貓。 此外,具有較短行的指令碼—— 預設值仍為 80 個字元——在普通編輯器中也更容易閱讀和理解!
我真的很喜歡 Bash。 我可以花幾個小時寫這篇文章或與其他愛好者交流優秀的技巧。 就希望你們能在評論中留下讚美。