有部分人不太願意定製自己的終端配置,因為:「伺服器太多,怎麼可能每臺都去定製,所以都用默認配置,習慣了就好」。其實道理很簡單,算筆賬就清楚了,除非你是 SA 每天管理上千臺伺服器,程序員的話,每天接觸的開發伺服器也就五臺以內。既然 90% 的利益都在那三五臺機器上,還在糾結 10% 的事情,這就叫不明智。

還有人擔心這 10% 的時間偶爾到裸環境下不適應了,所以拒絕 90% 的時間使用高級配置。這是我聽過最荒謬的理由,我天天自己開車上下班,偶爾騎下自行車我也不會忘記怎麼騎車。更不因為偶爾需要時怕不會騎了而把汽車賣了每天都堅持騎單車,或者乾脆就拒絕學汽車駕駛,拒絕提高自己的車技。我路由器上連 bash 都沒有,只有個 busybox 的殘缺 shell ,照著理由我要去遷就路由器麼?這種說法要不就是看不清楚自己核心利益在哪裡,要不就是沒體驗過汽車快起來可以比單車快幾倍。

何況不管是程序員還是 SA,做好配置的同步工作也就行了。如果可以花固定的時間,讓終端工作效率提升一倍以上,這種一次性的投資為何不做呢?所以接下來討論下終端環境下各種配置應該如何管理,如何同步的。

原則-1:託管你的配置

配置需要反覆錘鍊和迭代,迭代就需要持久化的文件託管和版本控制,不能說每次都憑藉記憶從頭寫一次,這樣你的配置永遠積累不下來。

所以從 Github 上新建個 config 項目開始,把各種:編輯器,shell,readline,tmux 配置一點點的放上去,新環境中克隆下來,放到一個安全的位置,比如 ~/.local/config 下面。

原則-2:同步到常用伺服器即可

如果新登陸一臺新伺服器,只是為了臨時操作一下,那大可不必同步你的配置。只有你判斷今後一段時間會反覆的在這臺伺服器上工作,那麼就花兩分鐘同步一下,你甚至可以把你配置的克隆和部署寫到一個 bootstrap.sh 文件上,curl 下來一執行,一句話的事情:

sh -c "$(curl -fsSL https://。。。/bootstrap.sh)"

這個腳本將會建立必要的目錄,克隆你的配置,再做一些必要的初始化,來到一臺新機器就跑這麼一行代碼,還有人覺得比你部署其他程序複雜麼?

原則-3:盡量少用軟連接

那麼最基本的 .bashrc 文件應該怎麼同步呢?第一種方法是將該文件做成一個軟連接指向你倉庫裏的實際的 bashrc 文件;第二種做法是在配置倉庫中寫一個 update.sh 腳本,自動拷貝倉庫裏的 bashrc 過去覆蓋 ~/.bashrc

前一種方式的問題是本地想做一些臨時修改就容易改動到倉庫裏的源文件把倉庫弄髒,這樣你後面更新的時候就需要 merge,或者選擇先提交。第二種方式的問題是每次更新了倉庫,運行 update.sh 就會把原來的 ~/.bashrc 給覆蓋掉了,所有本地化配置和臨時修改也就全部都沒了。

如何同步配置呢?

更合理的做法是新建一個:init.sh 用倉庫託管起來,而本地 ~/.bashrc 末尾加一句話:

source ~/.local/xxx/init.sh

即可,這個給文件末尾追加一句話的事情,可以讓前面的 bootstrap.sh 來承擔。

這樣你的通用配置被放到了倉庫裏的 init.sh 裡面,而本地化的一些臨時配置,還可以接著在 ~/.bashrc 其他部分寫,同時改寫 ~/.bashrc 不會把 config 倉庫弄髒;而更新 config 倉庫也不會把本地配置覆蓋沒。

更重要的是,init.sh 可以寫成同時兼容:sh, bash, zsh, dash 的模式,每個 shell的配置裡面只要 source 它一下就行了,那麼 init.sh 裡面即可寫通用所有 shell 的一些初始化工作,又可以針對不同的 shell 寫一些初始化配置。

對於實驗性的新配置,寫到本地配置裏即可,等你用一段時間,覺得好用了,再把它挪到公共配置倉庫裏固化起來。這樣隨著時間的積累,你的 init.sh 積累的配置越來越多,shell 越來越順手。

所以你並不需要託管你的 bashrc,你需要的是一個有版本管理的,可以四處同步的 init.sh。有恆產纔能有恆心,如果你每換一個環境都要從頭寫你的配置,但當然沒什麼心情寫下去;而如果你把配置固化託管到 github 上,四處都能同步使用,你才會隔三岔五的想著去優化迭代。

文末參考

初期建議全部寫在 init.sh 裡面的,後面複雜了可以進行模塊化拆分,現在我的 init.sh 現在基本就是一個入口。可以到 github 上搜索 bash 類型,星星最高的配置,或者按名字 dotfiles 搜索,我的就不拿出來獻醜了,寫的比我好的多的是。

Shell 方面我唯一可以一看的項目是我的 《Bash 中文速查表》:

github.com/skywind3000/

目前全網最全的 bash 簡明幫助,或許在你寫配置時可以參考用到。

(完結)

--

附:我當前的 init.sh (其實沒啥內容了,已經被拆分成只剩一個入口了)

# 互動式模式的初始化腳本
# 防止被載入兩次
if [ -z "$_INIT_SH_LOADED" ]; then
_INIT_SH_LOADED=1
else
return
fi

# 如果是非互動式則退出,比如 bash test.sh 這種調用 bash 運行腳本時就不是互動式
# 只有直接敲 bash 進入的等待用戶輸入命令的那種模式才成為互動式,才往下初始化
case "$-" in
*i*) ;;
*) return
esac

# 將個人 ~/.local/bin 目錄加入 PATH
if [ -d "$HOME/.local/bin" ]; then
export PATH="$HOME/.local/bin:$PATH"
fi

# 判斷 ~/.local/etc/config.sh 存在的話,就 source 它一下
if [ -f "$HOME/.local/etc/config.sh" ]; then
. "$HOME/.local/etc/config.sh"
fi

# 判斷 ~/.local/etc/local.sh 存在的話,就 source 它一下
if [ -f "$HOME/.local/etc/local.sh" ]; then
. "$HOME/.local/etc/local.sh"
fi

# 整理 PATH,刪除重複路徑
if [ -n "$PATH" ]; then
old_PATH=$PATH:; PATH=
while [ -n "$old_PATH" ]; do
x=${old_PATH%%:*}
case $PATH: in
*:"$x":*) ;;
*) PATH=$PATH:$x;;
esac
old_PATH=${old_PATH#*:}
done
PATH=${PATH#:}
unset old_PATH x
fi

export PATH

# 如果是 bash/zsh 的話,source 一下 ~/.local/etc/function.sh
if [ -n "$BASH_VERSION" ] || [ -n "$ZSH_VERSION" ]; then
# run script for interactive mode of bash/zsh
if [[ $- == *i* ]] && [ -z "$_INIT_SH_NOFUN" ]; then
if [ -f "$HOME/.local/etc/function.sh" ]; then
. "$HOME/.local/etc/function.sh"
fi
fi
fi

# 如果是登陸模式,那麼 source 一下 ~/.local/etc/login.sh
if [ -n "$BASH_VERSION" ]; then
if shopt -q login_shell; then
if [ -f "$HOME/.local/etc/login.sh" ] && [ -z "$_INIT_SH_NOLOG" ]; then
. "$HOME/.local/etc/login.sh"
fi
fi
elif [ -n "$ZSH_VERSION" ]; then
if [[ -o login ]]; then
if [ -f "$HOME/.local/etc/login.sh" ] && [ -z "$_INIT_SH_NOLOG" ]; then
. "$HOME/.local/etc/login.sh"
fi
fi
fi

--


推薦閱讀:
相關文章