<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Shell on Tarragon</title><link>https://tarrragon.github.io/blog/tags/shell/</link><description>Recent content in Shell on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 01 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/shell/index.xml" rel="self" type="application/rss+xml"/><item><title>安裝過程用到的基礎操作</title><link>https://tarrragon.github.io/blog/linux/install/basic-operations/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/install/basic-operations/</guid><description>&lt;p>這篇是「Linux 安裝與機器初始化」系列的基礎操作篇，系列用一套最小化的 Arch Linux 安裝當貫穿例子。在安裝、補工具、設定 SSH 的過程中，會用到一小撮基礎操作——成為 root、用 nano 改設定檔、shell 的幾個符號。Linux 的指令入門教學網路上已經很豐富，這篇不重複那些，只挑「這個系列實際用到、而且沒太多 Linux 實作經驗的人容易卡住」的那幾個介紹清楚，讓你照著操作時不會被一個沒見過的指令擋在半路。已經熟的，直接跳到 &lt;a href="../install-option-decisions/">安裝選項判讀&lt;/a>。&lt;/p>
&lt;h2 id="成為-rootsu--">成為 root：su -&lt;/h2>
&lt;p>&lt;code>su -&lt;/code> 讓你從一般使用者切換成 root（系統管理員），整個 session 都以 root 身分操作。這個系列用到它，是因為在還沒有 &lt;code>sudo&lt;/code> 的最小系統上，要裝 sudo、改系統設定，得先成為 root——而成為 root 的方式就是 &lt;code>su -&lt;/code>，輸入 root 密碼後進入 root 的 shell。&lt;/p>
&lt;p>那個 &lt;code>-&lt;/code> 決定你載入哪一套環境。&lt;code>su -&lt;/code> 啟動一個 login shell（模擬從頭登入、會跑完整的登入環境初始化），載入 root 自己的完整環境——把 &lt;code>PATH&lt;/code>（shell 搜尋指令的目錄清單）換成 root 的（會包含 &lt;code>/usr/sbin&lt;/code> 這類放管理工具的目錄）、工作目錄切到 root 的家。&lt;code>su&lt;/code>（不加 &lt;code>-&lt;/code>）則不啟動 login shell，環境多半沿用你呼叫時的、可能少掉那些管理工具目錄，於是有些管理指令會因為不在 &lt;code>PATH&lt;/code> 裡而「找不到」，明明你已經是 root。所以要做系統管理，習慣用 &lt;code>su -&lt;/code>。&lt;/p>
&lt;p>&lt;code>su -&lt;/code> 跟 &lt;code>sudo&lt;/code> 解決的是不同情境。&lt;code>sudo&lt;/code> 是「以 root 身分跑單一一條指令」，跑完就回到你自己；&lt;code>su -&lt;/code> 是「整段都當 root」。這個系列先用 &lt;code>su -&lt;/code> 是因為 sudo 還沒裝——一旦 sudo 裝好、wheel 群組（慣例上被授權可用 sudo 的群組）的授權設好，後面就改用 &lt;code>sudo &amp;lt;指令&amp;gt;&lt;/code>，不再整段切 root。做完 root 的事，打 &lt;code>exit&lt;/code> 回到原本的使用者。&lt;/p>
&lt;h2 id="用-nano-改設定檔">用 nano 改設定檔&lt;/h2>
&lt;p>nano 是一個對照 vi 更直覺的文字編輯器，安裝過程改 &lt;code>locale.gen&lt;/code>、&lt;code>hostname&lt;/code>、&lt;code>sudoers&lt;/code> 這些設定檔時會用到它。它的好處是所有快捷鍵都列在畫面最下面兩行，不必背。&lt;/p>
&lt;p>那兩行裡的 &lt;code>^&lt;/code> 符號代表 Ctrl 鍵。&lt;code>^O&lt;/code> 就是 Ctrl+O、&lt;code>^X&lt;/code> 就是 Ctrl+X。這個系列用到的幾個：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>按鍵&lt;/th>
 &lt;th>畫面標示&lt;/th>
 &lt;th>作用&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Ctrl+O&lt;/td>
 &lt;td>&lt;code>^O Write Out&lt;/code>&lt;/td>
 &lt;td>存檔（會問檔名，按 Enter 確認）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Ctrl+X&lt;/td>
 &lt;td>&lt;code>^X Exit&lt;/code>&lt;/td>
 &lt;td>離開（有未存變更會問要不要存）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Ctrl+W&lt;/td>
 &lt;td>&lt;code>^W Where Is&lt;/code>&lt;/td>
 &lt;td>搜尋——在長檔案裡跳到某個字串&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Ctrl+K&lt;/td>
 &lt;td>&lt;code>^K Cut&lt;/code>&lt;/td>
 &lt;td>剪掉游標所在的整行&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Ctrl+U&lt;/td>
 &lt;td>&lt;code>^U Paste&lt;/code>&lt;/td>
 &lt;td>把剪掉的內容貼回來&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>把這幾個串起來就是一次典型的設定檔編輯。以這個系列裡「解開 &lt;code>locale.gen&lt;/code> 某一行的註解」為例：按 Ctrl+W 搜 &lt;code>en_US.UTF-8 UTF-8&lt;/code> 跳到那行、用 Backspace 刪掉行首的 &lt;code>#&lt;/code>、按 Ctrl+O 再 Enter 存檔、按 Ctrl+X 離開。改 &lt;code>hostname&lt;/code> 則是 Ctrl+K 剪掉預設那行、打上新主機名、Ctrl+O、Ctrl+X。Ctrl+K 加 Ctrl+U 合起來就是「剪下再貼上」，是搬移整行的常見組合。&lt;/p>
&lt;h2 id="檔名與指令的大小寫">檔名與指令的大小寫&lt;/h2>
&lt;p>Linux 把大小寫當成不同的字元，這對檔名跟指令都成立。&lt;code>Setup&lt;/code> 跟 &lt;code>setup&lt;/code> 是兩個不同的東西、&lt;code>Documents&lt;/code> 跟 &lt;code>documents&lt;/code> 是兩個不同的資料夾、打 &lt;code>Sudo&lt;/code> 不會執行到 &lt;code>sudo&lt;/code>。這條規則貫穿整個系列：執行 archboot 的安裝程式是 &lt;code>setup&lt;/code>（全小寫），啟動 Hyprland 桌面的指令是 &lt;code>Hyprland&lt;/code>（首字母大寫），兩者差一個字母的大小寫就是不同的目標。&lt;/p>
&lt;p>這對從 macOS 或 Windows 過來的人尤其常見，因為那兩個系統的預設檔案系統不分大小寫——在 Mac 上 &lt;code>File.txt&lt;/code> 跟 &lt;code>file.txt&lt;/code> 指向同一個檔，到了 Linux 就是兩個檔。同一個專案在 Mac 上跑得好好的，搬到 Linux 卻出現「檔案找不到」，常常就是某處大小寫對不上、而 Mac 的不分大小寫把問題藏了起來。&lt;/p>
&lt;p>判讀方式很簡單：在 Linux 上，指令、檔名、路徑一律照原樣的大小寫打。錯誤訊息 &lt;code>command not found&lt;/code> 或 &lt;code>No such file or directory&lt;/code> 在你確定東西明明就在時，先懷疑大小寫。&lt;/p></description><content:encoded><![CDATA[<p>這篇是「Linux 安裝與機器初始化」系列的基礎操作篇，系列用一套最小化的 Arch Linux 安裝當貫穿例子。在安裝、補工具、設定 SSH 的過程中，會用到一小撮基礎操作——成為 root、用 nano 改設定檔、shell 的幾個符號。Linux 的指令入門教學網路上已經很豐富，這篇不重複那些，只挑「這個系列實際用到、而且沒太多 Linux 實作經驗的人容易卡住」的那幾個介紹清楚，讓你照著操作時不會被一個沒見過的指令擋在半路。已經熟的，直接跳到 <a href="../install-option-decisions/">安裝選項判讀</a>。</p>
<h2 id="成為-rootsu--">成為 root：su -</h2>
<p><code>su -</code> 讓你從一般使用者切換成 root（系統管理員），整個 session 都以 root 身分操作。這個系列用到它，是因為在還沒有 <code>sudo</code> 的最小系統上，要裝 sudo、改系統設定，得先成為 root——而成為 root 的方式就是 <code>su -</code>，輸入 root 密碼後進入 root 的 shell。</p>
<p>那個 <code>-</code> 決定你載入哪一套環境。<code>su -</code> 啟動一個 login shell（模擬從頭登入、會跑完整的登入環境初始化），載入 root 自己的完整環境——把 <code>PATH</code>（shell 搜尋指令的目錄清單）換成 root 的（會包含 <code>/usr/sbin</code> 這類放管理工具的目錄）、工作目錄切到 root 的家。<code>su</code>（不加 <code>-</code>）則不啟動 login shell，環境多半沿用你呼叫時的、可能少掉那些管理工具目錄，於是有些管理指令會因為不在 <code>PATH</code> 裡而「找不到」，明明你已經是 root。所以要做系統管理，習慣用 <code>su -</code>。</p>
<p><code>su -</code> 跟 <code>sudo</code> 解決的是不同情境。<code>sudo</code> 是「以 root 身分跑單一一條指令」，跑完就回到你自己；<code>su -</code> 是「整段都當 root」。這個系列先用 <code>su -</code> 是因為 sudo 還沒裝——一旦 sudo 裝好、wheel 群組（慣例上被授權可用 sudo 的群組）的授權設好，後面就改用 <code>sudo &lt;指令&gt;</code>，不再整段切 root。做完 root 的事，打 <code>exit</code> 回到原本的使用者。</p>
<h2 id="用-nano-改設定檔">用 nano 改設定檔</h2>
<p>nano 是一個對照 vi 更直覺的文字編輯器，安裝過程改 <code>locale.gen</code>、<code>hostname</code>、<code>sudoers</code> 這些設定檔時會用到它。它的好處是所有快捷鍵都列在畫面最下面兩行，不必背。</p>
<p>那兩行裡的 <code>^</code> 符號代表 Ctrl 鍵。<code>^O</code> 就是 Ctrl+O、<code>^X</code> 就是 Ctrl+X。這個系列用到的幾個：</p>
<table>
  <thead>
      <tr>
          <th>按鍵</th>
          <th>畫面標示</th>
          <th>作用</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Ctrl+O</td>
          <td><code>^O Write Out</code></td>
          <td>存檔（會問檔名，按 Enter 確認）</td>
      </tr>
      <tr>
          <td>Ctrl+X</td>
          <td><code>^X Exit</code></td>
          <td>離開（有未存變更會問要不要存）</td>
      </tr>
      <tr>
          <td>Ctrl+W</td>
          <td><code>^W Where Is</code></td>
          <td>搜尋——在長檔案裡跳到某個字串</td>
      </tr>
      <tr>
          <td>Ctrl+K</td>
          <td><code>^K Cut</code></td>
          <td>剪掉游標所在的整行</td>
      </tr>
      <tr>
          <td>Ctrl+U</td>
          <td><code>^U Paste</code></td>
          <td>把剪掉的內容貼回來</td>
      </tr>
  </tbody>
</table>
<p>把這幾個串起來就是一次典型的設定檔編輯。以這個系列裡「解開 <code>locale.gen</code> 某一行的註解」為例：按 Ctrl+W 搜 <code>en_US.UTF-8 UTF-8</code> 跳到那行、用 Backspace 刪掉行首的 <code>#</code>、按 Ctrl+O 再 Enter 存檔、按 Ctrl+X 離開。改 <code>hostname</code> 則是 Ctrl+K 剪掉預設那行、打上新主機名、Ctrl+O、Ctrl+X。Ctrl+K 加 Ctrl+U 合起來就是「剪下再貼上」，是搬移整行的常見組合。</p>
<h2 id="檔名與指令的大小寫">檔名與指令的大小寫</h2>
<p>Linux 把大小寫當成不同的字元，這對檔名跟指令都成立。<code>Setup</code> 跟 <code>setup</code> 是兩個不同的東西、<code>Documents</code> 跟 <code>documents</code> 是兩個不同的資料夾、打 <code>Sudo</code> 不會執行到 <code>sudo</code>。這條規則貫穿整個系列：執行 archboot 的安裝程式是 <code>setup</code>（全小寫），啟動 Hyprland 桌面的指令是 <code>Hyprland</code>（首字母大寫），兩者差一個字母的大小寫就是不同的目標。</p>
<p>這對從 macOS 或 Windows 過來的人尤其常見，因為那兩個系統的預設檔案系統不分大小寫——在 Mac 上 <code>File.txt</code> 跟 <code>file.txt</code> 指向同一個檔，到了 Linux 就是兩個檔。同一個專案在 Mac 上跑得好好的，搬到 Linux 卻出現「檔案找不到」，常常就是某處大小寫對不上、而 Mac 的不分大小寫把問題藏了起來。</p>
<p>判讀方式很簡單：在 Linux 上，指令、檔名、路徑一律照原樣的大小寫打。錯誤訊息 <code>command not found</code> 或 <code>No such file or directory</code> 在你確定東西明明就在時，先懷疑大小寫。</p>
<h2 id="shell-的幾個符號">shell 的幾個符號</h2>
<p>這個系列的指令裡出現了幾個 shell 符號，它們是 shell 本身的語法、不是某個程式的參數，認得它們才讀得懂那些指令在做什麼。</p>
<p><code>&gt;</code> 跟 <code>&gt;&gt;</code> 是重導向，把本來會印到畫面的輸出改寫進檔案。<code>&gt;</code> 覆蓋、<code>&gt;&gt;</code> 追加。系列裡 <code>echo '%wheel ALL=(ALL:ALL) ALL' &gt; /etc/sudoers.d/10-wheel</code> 就是把那行文字寫進一個新檔，而設 <code>authorized_keys</code> 時用 <code>&gt;&gt;</code> 是為了追加、不洗掉既有的 key。</p>
<p><code>|</code>（管線）把左邊指令的輸出，直接餵給右邊指令當輸入。傳 dotfile 進 VM 的 <code>tar czf - . | ssh host 'tar xzf -'</code> 就是把 tar 打包的資料流，不落地直接透過 ssh 送到對面解開。</p>
<p><code>&amp;&amp;</code> 串接兩條指令，而且只有左邊成功才跑右邊。<code>cd ~/dotfiles &amp;&amp; ./scripts/install.sh</code> 的意思是「先切到目錄，切成功了才跑腳本」——如果目錄不存在、<code>cd</code> 失敗，後面的腳本就不會在錯的目錄下執行。</p>
<p><code>$(...)</code> 是命令替換，把括號裡指令的輸出，當成值填進當下這條指令。<code>chsh -s &quot;$(command -v zsh)&quot;</code> 會先跑 <code>command -v zsh</code> 得到 <code>/usr/bin/zsh</code>，再把這個路徑填給 <code>chsh</code>。理解這個語法，也才看得懂 <a href="../minimal-install-verify/">工具驗證篇</a> 講的 <code>which</code> 地雷：當 <code>which</code> 不存在、<code>$(which zsh)</code> 算出空字串，整條指令就拿到一個空值。</p>
<p>跟重導向相關的還有一個經典陷阱：<code>sudo echo '內容' &gt; /etc/某個-root-檔</code> 會失敗。原因是重導向 <code>&gt;</code> 由你的 shell 以你的身分執行，不是被 <code>sudo</code> 提權的那條指令——被提權的只有 <code>echo</code>，真正寫檔的還是你，而你沒權限寫那個 root 檔。解法是 <code>echo '內容' | sudo tee /etc/某個-root-檔</code>：<code>tee</code> 把流進它的 stdin 寫進檔案，而 <code>tee</code> 才是被 <code>sudo</code> 提權的那支，所以寫得進去。這個系列補 <code>sudoers</code> 檔時用的就是這個寫法。</p>
<p>權限對某些檔是硬要求，所以系列裡出現了 <code>chmod</code>。<code>chmod</code> 改檔案權限，那串數字是八進位的權限碼，三位分別代表擁有者/群組/其他人；每一位是讀（4）+ 寫（2）+ 執行（1）的和，所以 <code>7</code> 是全權、<code>6</code> 是可讀寫、<code>4</code> 是只讀、<code>0</code> 是無權。系列裡的 <code>chmod 440</code>（擁有者與群組可讀、其他人無權，給 sudoers 檔）、<code>chmod 600</code>（只有擁有者可讀寫，給私鑰）、<code>chmod 700</code>（只有擁有者全權，給 <code>.ssh</code> 目錄）就是這樣算出來的。這些檔對權限有要求——sudoers 必須是 440、私鑰必須只有自己讀得到，否則對應的工具會拒絕使用它們，所以那幾個 <code>chmod</code> 不是裝飾、是讓 sudo 跟 ssh 願意接受那些檔的條件。</p>
<h2 id="下一步">下一步</h2>
<p>認得這些基礎操作之後，就可以從 <a href="../install-option-decisions/">安裝選項判讀</a> 開始走完整的安裝流程，過程中再遇到這幾個操作就不會卡。</p>
]]></content:encoded></item><item><title>Zsh 模組化配置</title><link>https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/zsh-modular-config/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/zsh-modular-config/</guid><description>&lt;p>Shell 配置是 dotfile 管理裡最基礎也最常失控的一層。&lt;code>.zshrc&lt;/code> 或 &lt;code>.bashrc&lt;/code> 通常是開發者第一個開始客製的檔案，也是最容易長成數百行無結構巨檔的對象。&lt;/p>
&lt;h2 id="zsh-vs-bash-的配置檔載入順序">Zsh vs Bash 的配置檔載入順序&lt;/h2>
&lt;p>理解配置檔的載入順序是結構化拆分的前提。不知道哪個檔案在什麼時機被讀取，就無法判斷設定該放在哪。&lt;/p>
&lt;h3 id="bash-的載入順序">Bash 的載入順序&lt;/h3>
&lt;p>Bash 區分 login shell 和 non-login shell，兩者讀取的檔案不同：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Login shell&lt;/strong>（SSH 進來、&lt;code>bash --login&lt;/code>）：讀 &lt;code>~/.bash_profile&lt;/code>（如果不存在，依序嘗試 &lt;code>~/.bash_login&lt;/code> → &lt;code>~/.profile&lt;/code>）&lt;/li>
&lt;li>&lt;strong>Non-login interactive shell&lt;/strong>（開一個新終端機視窗）：讀 &lt;code>~/.bashrc&lt;/code>&lt;/li>
&lt;li>常見做法：在 &lt;code>~/.bash_profile&lt;/code> 裡 source &lt;code>~/.bashrc&lt;/code>，確保設定不管怎麼進來都一致&lt;/li>
&lt;/ul>
&lt;h3 id="zsh-的載入順序">Zsh 的載入順序&lt;/h3>
&lt;p>Zsh 的載入鏈比 Bash 更細緻：&lt;/p>
&lt;ol>
&lt;li>&lt;code>~/.zshenv&lt;/code> — 每次都讀（login、non-login、script 都會），放環境變數&lt;/li>
&lt;li>&lt;code>~/.zprofile&lt;/code> — 只有 login shell 讀，對應 Bash 的 &lt;code>~/.bash_profile&lt;/code>&lt;/li>
&lt;li>&lt;code>~/.zshrc&lt;/code> — interactive shell 讀，放 alias、function、prompt、plugin&lt;/li>
&lt;li>&lt;code>~/.zlogin&lt;/code> — login shell 在 &lt;code>.zshrc&lt;/code> 之後讀（少用）&lt;/li>
&lt;li>&lt;code>~/.zlogout&lt;/code> — logout 時讀（少用）&lt;/li>
&lt;/ol>
&lt;p>實務上 90% 的設定都進 &lt;code>.zshrc&lt;/code>，環境變數（&lt;code>PATH&lt;/code>、&lt;code>EDITOR&lt;/code>）放 &lt;code>.zshenv&lt;/code>。&lt;/p>
&lt;h2 id="結構化拆分從單一巨檔到模組化">結構化拆分：從單一巨檔到模組化&lt;/h2>
&lt;p>一個典型的失控 &lt;code>.zshrc&lt;/code> 長這樣：PATH 設定、alias、function、plugin 載入、prompt 配置、各種工具的 eval/source 全混在一起，改一個東西要在五百行裡找位置。&lt;/p>
&lt;p>模組化的目標是依職責拆檔，&lt;code>.zshrc&lt;/code> 本身只負責 source 這些模組：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># ~/.zshrc — 只做 source，不放具體設定&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="c1"># 環境變數（PATH 在 .zshenv，這裡放其他 interactive 專用的）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/env.zsh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># Alias&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/aliases.zsh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># Function&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/functions.zsh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># Plugin manager&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/plugins.zsh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="c1"># Prompt / theme&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/prompt.zsh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="c1"># 工具整合（fzf, nvm, pyenv, etc.）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/tools.zsh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="c1"># 機器專屬設定（不進 Git）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="o">[[&lt;/span> -f &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/local.zsh&amp;#34;&lt;/span> &lt;span class="o">]]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.config/zsh/local.zsh&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="各模組的職責">各模組的職責&lt;/h2>
&lt;h3 id="aliaseszsh--短指令對映">aliases.zsh — 短指令對映&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 檔案操作&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">ll&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;ls -alF&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">la&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;ls -A&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># Git 常用&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">gs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;git status&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">gd&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;git diff&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">gco&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;git checkout&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> &lt;span class="nv">gp&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;git push&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c1"># 導航&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> ..&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;cd ..&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="nb">alias&lt;/span> ...&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;cd ../..&amp;#39;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>判讀準則：alias 適合「不帶參數的簡單替換」。如果需要參數處理或條件判斷，改用 function。&lt;/p>
&lt;h3 id="functionszsh--帶邏輯的常用操作">functions.zsh — 帶邏輯的常用操作&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 建目錄並進入&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">mkcd&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> mkdir -p &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$1&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$1&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1"># 在 Git repo 根目錄搜尋&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">ggrep&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl"> git grep &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$@&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>git rev-parse --show-toplevel&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="o">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="toolszsh--第三方工具的初始化">tools.zsh — 第三方工具的初始化&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># fzf&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="o">[[&lt;/span> -f ~/.fzf.zsh &lt;span class="o">]]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">source&lt;/span> ~/.fzf.zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># nvm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">NVM_DIR&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.nvm&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="o">[[&lt;/span> -s &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$NVM_DIR&lt;/span>&lt;span class="s2">/nvm.sh&amp;#34;&lt;/span> &lt;span class="o">]]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">source&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$NVM_DIR&lt;/span>&lt;span class="s2">/nvm.sh&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># pyenv&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="nb">command&lt;/span> -v pyenv &amp;gt;/dev/null &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>pyenv init -&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>每個工具的 init 前面加存在性檢查（&lt;code>command -v&lt;/code> 或 &lt;code>[[ -f ]]&lt;/code>），避免在沒裝該工具的機器上報錯。&lt;/p></description><content:encoded><![CDATA[<p>Shell 配置是 dotfile 管理裡最基礎也最常失控的一層。<code>.zshrc</code> 或 <code>.bashrc</code> 通常是開發者第一個開始客製的檔案，也是最容易長成數百行無結構巨檔的對象。</p>
<h2 id="zsh-vs-bash-的配置檔載入順序">Zsh vs Bash 的配置檔載入順序</h2>
<p>理解配置檔的載入順序是結構化拆分的前提。不知道哪個檔案在什麼時機被讀取，就無法判斷設定該放在哪。</p>
<h3 id="bash-的載入順序">Bash 的載入順序</h3>
<p>Bash 區分 login shell 和 non-login shell，兩者讀取的檔案不同：</p>
<ul>
<li><strong>Login shell</strong>（SSH 進來、<code>bash --login</code>）：讀 <code>~/.bash_profile</code>（如果不存在，依序嘗試 <code>~/.bash_login</code> → <code>~/.profile</code>）</li>
<li><strong>Non-login interactive shell</strong>（開一個新終端機視窗）：讀 <code>~/.bashrc</code></li>
<li>常見做法：在 <code>~/.bash_profile</code> 裡 source <code>~/.bashrc</code>，確保設定不管怎麼進來都一致</li>
</ul>
<h3 id="zsh-的載入順序">Zsh 的載入順序</h3>
<p>Zsh 的載入鏈比 Bash 更細緻：</p>
<ol>
<li><code>~/.zshenv</code> — 每次都讀（login、non-login、script 都會），放環境變數</li>
<li><code>~/.zprofile</code> — 只有 login shell 讀，對應 Bash 的 <code>~/.bash_profile</code></li>
<li><code>~/.zshrc</code> — interactive shell 讀，放 alias、function、prompt、plugin</li>
<li><code>~/.zlogin</code> — login shell 在 <code>.zshrc</code> 之後讀（少用）</li>
<li><code>~/.zlogout</code> — logout 時讀（少用）</li>
</ol>
<p>實務上 90% 的設定都進 <code>.zshrc</code>，環境變數（<code>PATH</code>、<code>EDITOR</code>）放 <code>.zshenv</code>。</p>
<h2 id="結構化拆分從單一巨檔到模組化">結構化拆分：從單一巨檔到模組化</h2>
<p>一個典型的失控 <code>.zshrc</code> 長這樣：PATH 設定、alias、function、plugin 載入、prompt 配置、各種工具的 eval/source 全混在一起，改一個東西要在五百行裡找位置。</p>
<p>模組化的目標是依職責拆檔，<code>.zshrc</code> 本身只負責 source 這些模組：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># ~/.zshrc — 只做 source，不放具體設定</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># 環境變數（PATH 在 .zshenv，這裡放其他 interactive 專用的）</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/env.zsh&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># Alias</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/aliases.zsh&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># Function</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/functions.zsh&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># Plugin manager</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/plugins.zsh&#34;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"># Prompt / theme</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/prompt.zsh&#34;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"># 工具整合（fzf, nvm, pyenv, etc.）</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/tools.zsh&#34;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"># 機器專屬設定（不進 Git）</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="o">[[</span> -f <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/local.zsh&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.config/zsh/local.zsh&#34;</span></span></span></code></pre></div><h2 id="各模組的職責">各模組的職責</h2>
<h3 id="aliaseszsh--短指令對映">aliases.zsh — 短指令對映</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 檔案操作</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nb">alias</span> <span class="nv">ll</span><span class="o">=</span><span class="s1">&#39;ls -alF&#39;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nb">alias</span> <span class="nv">la</span><span class="o">=</span><span class="s1">&#39;ls -A&#39;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># Git 常用</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nb">alias</span> <span class="nv">gs</span><span class="o">=</span><span class="s1">&#39;git status&#39;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nb">alias</span> <span class="nv">gd</span><span class="o">=</span><span class="s1">&#39;git diff&#39;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nb">alias</span> <span class="nv">gco</span><span class="o">=</span><span class="s1">&#39;git checkout&#39;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nb">alias</span> <span class="nv">gp</span><span class="o">=</span><span class="s1">&#39;git push&#39;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 導航</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nb">alias</span> ..<span class="o">=</span><span class="s1">&#39;cd ..&#39;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nb">alias</span> ...<span class="o">=</span><span class="s1">&#39;cd ../..&#39;</span></span></span></code></pre></div><p>判讀準則：alias 適合「不帶參數的簡單替換」。如果需要參數處理或條件判斷，改用 function。</p>
<h3 id="functionszsh--帶邏輯的常用操作">functions.zsh — 帶邏輯的常用操作</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 建目錄並進入</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">mkcd<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    mkdir -p <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># 在 Git repo 根目錄搜尋</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">ggrep<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">    git grep <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="k">$(</span>git rev-parse --show-toplevel<span class="k">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="o">}</span></span></span></code></pre></div><h3 id="toolszsh--第三方工具的初始化">tools.zsh — 第三方工具的初始化</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># fzf</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="o">[[</span> -f ~/.fzf.zsh <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="nb">source</span> ~/.fzf.zsh
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># nvm</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">export</span> <span class="nv">NVM_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nvm&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="o">[[</span> -s <span class="s2">&#34;</span><span class="nv">$NVM_DIR</span><span class="s2">/nvm.sh&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="nb">source</span> <span class="s2">&#34;</span><span class="nv">$NVM_DIR</span><span class="s2">/nvm.sh&#34;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># pyenv</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="nb">command</span> -v pyenv &gt;/dev/null <span class="o">&amp;&amp;</span> <span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>pyenv init -<span class="k">)</span><span class="s2">&#34;</span></span></span></code></pre></div><p>每個工具的 init 前面加存在性檢查（<code>command -v</code> 或 <code>[[ -f ]]</code>），避免在沒裝該工具的機器上報錯。</p>
<h3 id="localzsh--機器專屬不進-git">local.zsh — 機器專屬、不進 Git</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 公司 VPN 設定</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">export</span> <span class="nv">CORP_PROXY</span><span class="o">=</span><span class="s2">&#34;http://proxy.corp:8080&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 只有這台機器需要的 PATH</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/corp-tools/bin:</span><span class="nv">$PATH</span><span class="s2">&#34;</span></span></span></code></pre></div><p>在 dotfile repo 的 <code>.gitignore</code> 裡排除這個檔案。<code>.zshrc</code> 裡用 <code>[[ -f ... ]] &amp;&amp; source</code> 確保不存在也不報錯。</p>
]]></content:encoded></item><item><title>Shell 背景 Process</title><link>https://tarrragon.github.io/blog/llm/knowledge-cards/shell-background-process/</link><pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/llm/knowledge-cards/shell-background-process/</guid><description>&lt;p>Shell 背景 Process 的核心概念是「terminal 啟動的程式何時跟 shell 綁定、何時可以脫離、被 shell 用什麼方式管理」。本地 LLM 場景中、&lt;code>ollama serve&lt;/code> 這類常駐 server 需要持續跑、放前景會把 terminal 卡住、放背景才能繼續打其他指令、或關掉 terminal 後讓服務改交給 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service&lt;/a> 接手。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Shell（zsh / bash）執行一個程式時、預設讓程式佔住 terminal、stdin / stdout / stderr 直接連到使用者眼前的視窗、稱為&lt;strong>前景 process&lt;/strong>。指令尾巴加 &lt;code>&amp;amp;&lt;/code> 改成&lt;strong>背景 process&lt;/strong>、shell 立刻拿回 prompt 控制權、程式繼續跑但不佔住 terminal。背景 process 仍綁在當前 shell session、關掉 terminal 視窗時通常會被 SIGHUP 終止；要完全脫離 shell 生命週期、得改用 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service&lt;/a> 或 &lt;code>nohup&lt;/code> / &lt;code>disown&lt;/code> 等機制。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>shell 控制 process 的關鍵操作：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>動作&lt;/th>
 &lt;th>指令 / 按鍵&lt;/th>
 &lt;th>效果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>前景跑&lt;/td>
 &lt;td>&lt;code>ollama serve&lt;/code>&lt;/td>
 &lt;td>terminal 被卡住、看到 process stdout&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>背景跑&lt;/td>
 &lt;td>&lt;code>ollama serve &amp;amp;&lt;/code>&lt;/td>
 &lt;td>拿回 prompt、程式仍在跑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>中止前景 process&lt;/td>
 &lt;td>&lt;code>Ctrl+C&lt;/code>&lt;/td>
 &lt;td>送 SIGINT、多數程式收到後優雅退出&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>暫停前景 process&lt;/td>
 &lt;td>&lt;code>Ctrl+Z&lt;/code>&lt;/td>
 &lt;td>送 SIGTSTP、process 進 stopped 狀態&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>列出當前 shell jobs&lt;/td>
 &lt;td>&lt;code>jobs&lt;/code>&lt;/td>
 &lt;td>看 shell 管理的背景 / 暫停 job&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>把 job 拉回前景&lt;/td>
 &lt;td>&lt;code>fg %1&lt;/code>&lt;/td>
 &lt;td>1 號 job 變前景&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>把暫停 job 改背景&lt;/td>
 &lt;td>&lt;code>bg %1&lt;/code>&lt;/td>
 &lt;td>1 號 job 改背景繼續跑&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>排錯常用的兩個工具（兩者跟 shell job 不直接相關、是 macOS 系統工具）：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>指令&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>lsof -i :11434&lt;/code>&lt;/td>
 &lt;td>找出哪個 process 在聽 11434 &lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/port-and-localhost/" data-link-title="Port 與 Localhost" data-link-desc="TCP port 與 listen address 如何決定 API server 的對外暴露範圍">port&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>pkill -f &amp;quot;ollama serve&amp;quot;&lt;/code>&lt;/td>
 &lt;td>用 pattern 匹配 process 命令列、送 SIGTERM 終止&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ps aux | grep ollama&lt;/code>&lt;/td>
 &lt;td>列出所有跟 ollama 有關的 process&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>對 macOS 新手最常遇到的兩個事故：一個是「前景跑 server 後不知道怎麼脫身」、解法是 &lt;code>Ctrl+Z&lt;/code> 暫停 + &lt;code>bg&lt;/code> 改背景、或下次改用 &lt;code>&amp;amp;&lt;/code> 啟動；另一個是「pkill 沒指定夠精確的 pattern、誤殺其他 process」、解法是先用 &lt;code>ps aux&lt;/code> 加 &lt;code>grep&lt;/code> 確認 PID 再 kill。&lt;/p></description><content:encoded><![CDATA[<p>Shell 背景 Process 的核心概念是「terminal 啟動的程式何時跟 shell 綁定、何時可以脫離、被 shell 用什麼方式管理」。本地 LLM 場景中、<code>ollama serve</code> 這類常駐 server 需要持續跑、放前景會把 terminal 卡住、放背景才能繼續打其他指令、或關掉 terminal 後讓服務改交給 <a href="/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service</a> 接手。</p>
<h2 id="概念位置">概念位置</h2>
<p>Shell（zsh / bash）執行一個程式時、預設讓程式佔住 terminal、stdin / stdout / stderr 直接連到使用者眼前的視窗、稱為<strong>前景 process</strong>。指令尾巴加 <code>&amp;</code> 改成<strong>背景 process</strong>、shell 立刻拿回 prompt 控制權、程式繼續跑但不佔住 terminal。背景 process 仍綁在當前 shell session、關掉 terminal 視窗時通常會被 SIGHUP 終止；要完全脫離 shell 生命週期、得改用 <a href="/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service</a> 或 <code>nohup</code> / <code>disown</code> 等機制。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>shell 控制 process 的關鍵操作：</p>
<table>
  <thead>
      <tr>
          <th>動作</th>
          <th>指令 / 按鍵</th>
          <th>效果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>前景跑</td>
          <td><code>ollama serve</code></td>
          <td>terminal 被卡住、看到 process stdout</td>
      </tr>
      <tr>
          <td>背景跑</td>
          <td><code>ollama serve &amp;</code></td>
          <td>拿回 prompt、程式仍在跑</td>
      </tr>
      <tr>
          <td>中止前景 process</td>
          <td><code>Ctrl+C</code></td>
          <td>送 SIGINT、多數程式收到後優雅退出</td>
      </tr>
      <tr>
          <td>暫停前景 process</td>
          <td><code>Ctrl+Z</code></td>
          <td>送 SIGTSTP、process 進 stopped 狀態</td>
      </tr>
      <tr>
          <td>列出當前 shell jobs</td>
          <td><code>jobs</code></td>
          <td>看 shell 管理的背景 / 暫停 job</td>
      </tr>
      <tr>
          <td>把 job 拉回前景</td>
          <td><code>fg %1</code></td>
          <td>1 號 job 變前景</td>
      </tr>
      <tr>
          <td>把暫停 job 改背景</td>
          <td><code>bg %1</code></td>
          <td>1 號 job 改背景繼續跑</td>
      </tr>
  </tbody>
</table>
<p>排錯常用的兩個工具（兩者跟 shell job 不直接相關、是 macOS 系統工具）：</p>
<table>
  <thead>
      <tr>
          <th>指令</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>lsof -i :11434</code></td>
          <td>找出哪個 process 在聽 11434 <a href="/blog/llm/knowledge-cards/port-and-localhost/" data-link-title="Port 與 Localhost" data-link-desc="TCP port 與 listen address 如何決定 API server 的對外暴露範圍">port</a></td>
      </tr>
      <tr>
          <td><code>pkill -f &quot;ollama serve&quot;</code></td>
          <td>用 pattern 匹配 process 命令列、送 SIGTERM 終止</td>
      </tr>
      <tr>
          <td><code>ps aux | grep ollama</code></td>
          <td>列出所有跟 ollama 有關的 process</td>
      </tr>
  </tbody>
</table>
<p>對 macOS 新手最常遇到的兩個事故：一個是「前景跑 server 後不知道怎麼脫身」、解法是 <code>Ctrl+Z</code> 暫停 + <code>bg</code> 改背景、或下次改用 <code>&amp;</code> 啟動；另一個是「pkill 沒指定夠精確的 pattern、誤殺其他 process」、解法是先用 <code>ps aux</code> 加 <code>grep</code> 確認 PID 再 kill。</p>
<h2 id="設計責任">設計責任</h2>
<p>選前景 vs 背景的判讀：debug 場景前景跑、能直接看到 log；日常使用改 <a href="/blog/llm/knowledge-cards/launchd-service/" data-link-title="launchd Service" data-link-desc="macOS 原生的服務管理機制、把 process 註冊成自動啟動的 daemon 或 agent">launchd service</a> 跑、跟 shell session 完全脫鉤。<code>&amp;</code> 適合「terminal 開著就讓它跑、關掉也沒關係」的臨時場景、不適合需要長期穩定的服務。排錯時養成「先 <code>lsof</code> 找誰佔資源、再 <code>ps</code> 確認身分、最後才 kill」的順序、避免誤殺。</p>
]]></content:encoded></item><item><title>PATH、Plugin 與 Prompt</title><link>https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/path-plugin-prompt/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/path-plugin-prompt/</guid><description>&lt;p>PATH、plugin manager 和 prompt 是 shell 配置裡「每個開發者都會碰到、但容易放任不管」的三個區域。&lt;/p>
&lt;h2 id="path-管理">PATH 管理&lt;/h2>
&lt;p>PATH 是最容易腐化的環境變數——每裝一個工具就加一條，最後 PATH 變成一長串看不懂的路徑，順序還會互相影響。&lt;/p>
&lt;p>管理原則：&lt;/p>
&lt;ul>
&lt;li>PATH 設定集中在一個地方（&lt;code>.zshenv&lt;/code> 或 &lt;code>env.zsh&lt;/code>），不散落在多個檔案&lt;/li>
&lt;li>新增前先想：這個路徑是所有機器都需要、還是特定機器才需要？共用的進 env.zsh，特定的進 local.zsh&lt;/li>
&lt;li>用 &lt;code>typeset -U PATH&lt;/code> (Zsh) 自動去除重複項目，避免多次 source 導致 PATH 不斷加長&lt;/li>
&lt;/ul>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># ~/.config/zsh/env.zsh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">typeset&lt;/span> -U PATH &lt;span class="c1"># 去重&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 自己的 script&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.local/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="nb">export&lt;/span> &lt;span class="nv">PATH&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/bin:&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="plugin-manager-選型">Plugin Manager 選型&lt;/h2>
&lt;p>Zsh plugin manager 的選擇很多，差異主要在載入速度和功能豐富度：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>無 plugin manager&lt;/strong>：直接 git clone plugin 到某個目錄，手動 source。最簡單、最透明、但更新要自己管&lt;/li>
&lt;li>&lt;strong>zinit&lt;/strong>（原 zplugin）：載入速度最快（turbo mode 延遲載入）、功能最多、但配置語法學習曲線高&lt;/li>
&lt;li>&lt;strong>antidote&lt;/strong>：宣告式（一個 &lt;code>.zsh_plugins.txt&lt;/code> 列出所有 plugin），概念簡單&lt;/li>
&lt;li>&lt;strong>sheldon&lt;/strong>：Rust 寫的、速度快、設定用 TOML&lt;/li>
&lt;/ul>
&lt;p>常用 plugin：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>zsh-autosuggestions&lt;/strong>：根據歷史指令自動補全建議（灰色字，按右箭頭接受）&lt;/li>
&lt;li>&lt;strong>zsh-syntax-highlighting&lt;/strong>：指令行即時語法高亮&lt;/li>
&lt;li>&lt;strong>zsh-completions&lt;/strong>：額外的 tab 補全定義&lt;/li>
&lt;/ul>
&lt;h2 id="prompt-設計">Prompt 設計&lt;/h2>
&lt;p>Prompt 是每次按 Enter 都會看到的東西，值得花時間設計但不需要複雜。&lt;/p>
&lt;p>基本款（不用框架）：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 顯示目錄 + git branch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">autoload -Uz vcs_info
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">precmd&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span> vcs_info &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">zstyle &lt;span class="s1">&amp;#39;:vcs_info:git:*&amp;#39;&lt;/span> formats &lt;span class="s1">&amp;#39; (%b)&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="nv">PROMPT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;%F{blue}%~%f%F{green}${vcs_info_msg_0_}%f %# &amp;#39;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>框架款：Starship（跨 shell、用 TOML 設定、Rust 寫的速度快）是目前最常被推薦的 prompt 工具。它的配置進 &lt;code>~/.config/starship.toml&lt;/code>，也是 dotfile 的一部分。&lt;/p>
&lt;h2 id="dotfile-結構對應">Dotfile 結構對應&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">管理工具與目錄結構&lt;/a>裡的 stow 目錄結構，shell 配置的對應：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">~/dotfiles/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">└── zsh/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> ├── .zshenv
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> ├── .zshrc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> └── .config/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> └── zsh/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> ├── aliases.zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> ├── functions.zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> ├── plugins.zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> ├── prompt.zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> ├── tools.zsh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> └── env.zsh&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>stow zsh&lt;/code> 會在家目錄建立 &lt;code>.zshenv&lt;/code> 和 &lt;code>.zshrc&lt;/code> 的 symlink，在 &lt;code>.config/zsh/&lt;/code> 下建立各模組檔案的 symlink。&lt;code>local.zsh&lt;/code> 不在 repo 裡，各機器自己建。&lt;/p></description><content:encoded><![CDATA[<p>PATH、plugin manager 和 prompt 是 shell 配置裡「每個開發者都會碰到、但容易放任不管」的三個區域。</p>
<h2 id="path-管理">PATH 管理</h2>
<p>PATH 是最容易腐化的環境變數——每裝一個工具就加一條，最後 PATH 變成一長串看不懂的路徑，順序還會互相影響。</p>
<p>管理原則：</p>
<ul>
<li>PATH 設定集中在一個地方（<code>.zshenv</code> 或 <code>env.zsh</code>），不散落在多個檔案</li>
<li>新增前先想：這個路徑是所有機器都需要、還是特定機器才需要？共用的進 env.zsh，特定的進 local.zsh</li>
<li>用 <code>typeset -U PATH</code> (Zsh) 自動去除重複項目，避免多次 source 導致 PATH 不斷加長</li>
</ul>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># ~/.config/zsh/env.zsh</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">typeset</span> -U PATH  <span class="c1"># 去重</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 自己的 script</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/bin:</span><span class="nv">$PATH</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/bin:</span><span class="nv">$PATH</span><span class="s2">&#34;</span></span></span></code></pre></div><h2 id="plugin-manager-選型">Plugin Manager 選型</h2>
<p>Zsh plugin manager 的選擇很多，差異主要在載入速度和功能豐富度：</p>
<ul>
<li><strong>無 plugin manager</strong>：直接 git clone plugin 到某個目錄，手動 source。最簡單、最透明、但更新要自己管</li>
<li><strong>zinit</strong>（原 zplugin）：載入速度最快（turbo mode 延遲載入）、功能最多、但配置語法學習曲線高</li>
<li><strong>antidote</strong>：宣告式（一個 <code>.zsh_plugins.txt</code> 列出所有 plugin），概念簡單</li>
<li><strong>sheldon</strong>：Rust 寫的、速度快、設定用 TOML</li>
</ul>
<p>常用 plugin：</p>
<ul>
<li><strong>zsh-autosuggestions</strong>：根據歷史指令自動補全建議（灰色字，按右箭頭接受）</li>
<li><strong>zsh-syntax-highlighting</strong>：指令行即時語法高亮</li>
<li><strong>zsh-completions</strong>：額外的 tab 補全定義</li>
</ul>
<h2 id="prompt-設計">Prompt 設計</h2>
<p>Prompt 是每次按 Enter 都會看到的東西，值得花時間設計但不需要複雜。</p>
<p>基本款（不用框架）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 顯示目錄 + git branch</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">autoload -Uz vcs_info
</span></span><span class="line"><span class="ln">3</span><span class="cl">precmd<span class="o">()</span> <span class="o">{</span> vcs_info <span class="o">}</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">zstyle <span class="s1">&#39;:vcs_info:git:*&#39;</span> formats <span class="s1">&#39; (%b)&#39;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nv">PROMPT</span><span class="o">=</span><span class="s1">&#39;%F{blue}%~%f%F{green}${vcs_info_msg_0_}%f %# &#39;</span></span></span></code></pre></div><p>框架款：Starship（跨 shell、用 TOML 設定、Rust 寫的速度快）是目前最常被推薦的 prompt 工具。它的配置進 <code>~/.config/starship.toml</code>，也是 dotfile 的一部分。</p>
<h2 id="dotfile-結構對應">Dotfile 結構對應</h2>
<p><a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">管理工具與目錄結構</a>裡的 stow 目錄結構，shell 配置的對應：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">~/dotfiles/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">└── zsh/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    ├── .zshenv
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    ├── .zshrc
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    └── .config/
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        └── zsh/
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">            ├── aliases.zsh
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            ├── functions.zsh
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">            ├── plugins.zsh
</span></span><span class="line"><span class="ln">10</span><span class="cl">            ├── prompt.zsh
</span></span><span class="line"><span class="ln">11</span><span class="cl">            ├── tools.zsh
</span></span><span class="line"><span class="ln">12</span><span class="cl">            └── env.zsh</span></span></code></pre></div><p><code>stow zsh</code> 會在家目錄建立 <code>.zshenv</code> 和 <code>.zshrc</code> 的 symlink，在 <code>.config/zsh/</code> 下建立各模組檔案的 symlink。<code>local.zsh</code> 不在 repo 裡，各機器自己建。</p>
]]></content:encoded></item><item><title>模組二：Shell 配置</title><link>https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/</guid><description>&lt;p>Shell 配置是 dotfile 管理裡最基礎也最常失控的一層。&lt;code>.zshrc&lt;/code> 或 &lt;code>.bashrc&lt;/code> 通常是開發者第一個開始客製的檔案，也是最容易長成數百行無結構巨檔的對象。這個模組教的是怎麼把 shell 配置拆成可維護的模組化結構。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/zsh-modular-config/" data-link-title="Zsh 模組化配置" data-link-desc=".zshrc 長到數百行不敢動時回來讀">Zsh 模組化配置&lt;/a>&lt;/td>
 &lt;td>zsh/bash 載入順序、.zshrc 只做 source 的拆分結構、各模組的職責&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/02-shell-config/path-plugin-prompt/" data-link-title="PATH、Plugin 與 Prompt" data-link-desc="PATH 越來越長不知道怎麼管、要選 zsh plugin manager、或想設計 prompt 時回來讀">PATH、Plugin 與 Prompt&lt;/a>&lt;/td>
 &lt;td>PATH 管理原則、plugin manager 選型、prompt 設計、dotfile 目錄結構對應&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/management-strategies/" data-link-title="管理策略與選型" data-link-desc="要選 dotfile 管理工具時回來讀 — bare repo、stow、chezmoi 的適用場景與選型判讀">模組一：管理策略&lt;/a>：stow 的 package 概念怎麼對應 shell 配置的目錄結構&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/cross-platform-one-repo/" data-link-title="跨平台共用一個 Repo" data-link-desc="macOS 跟 Linux 要共用同一個 dotfile repo、不想維護兩份時回來讀">模組一：跨平台共用一個 Repo&lt;/a>：path.zsh 和 tools.zsh 裡的 OS 分流做法&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器&lt;/a>：terminal emulator 的配色跟 shell prompt 的配色協調&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Shell 配置是 dotfile 管理裡最基礎也最常失控的一層。<code>.zshrc</code> 或 <code>.bashrc</code> 通常是開發者第一個開始客製的檔案，也是最容易長成數百行無結構巨檔的對象。這個模組教的是怎麼把 shell 配置拆成可維護的模組化結構。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/02-shell-config/zsh-modular-config/" data-link-title="Zsh 模組化配置" data-link-desc=".zshrc 長到數百行不敢動時回來讀">Zsh 模組化配置</a></td>
          <td>zsh/bash 載入順序、.zshrc 只做 source 的拆分結構、各模組的職責</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/02-shell-config/path-plugin-prompt/" data-link-title="PATH、Plugin 與 Prompt" data-link-desc="PATH 越來越長不知道怎麼管、要選 zsh plugin manager、或想設計 prompt 時回來讀">PATH、Plugin 與 Prompt</a></td>
          <td>PATH 管理原則、plugin manager 選型、prompt 設計、dotfile 目錄結構對應</td>
      </tr>
  </tbody>
</table>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/linux/dotfile/01-dotfile-management/management-strategies/" data-link-title="管理策略與選型" data-link-desc="要選 dotfile 管理工具時回來讀 — bare repo、stow、chezmoi 的適用場景與選型判讀">模組一：管理策略</a>：stow 的 package 概念怎麼對應 shell 配置的目錄結構</li>
<li>→ <a href="/blog/linux/dotfile/01-dotfile-management/cross-platform-one-repo/" data-link-title="跨平台共用一個 Repo" data-link-desc="macOS 跟 Linux 要共用同一個 dotfile repo、不想維護兩份時回來讀">模組一：跨平台共用一個 Repo</a>：path.zsh 和 tools.zsh 裡的 OS 分流做法</li>
<li>→ <a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器</a>：terminal emulator 的配色跟 shell prompt 的配色協調</li>
</ul>
]]></content:encoded></item></channel></rss>