<?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>Bash on Tarragon</title><link>https://tarrragon.github.io/blog/tags/bash/</link><description>Recent content in Bash on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 29 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/bash/index.xml" rel="self" type="application/rss+xml"/><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 配置</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><item><title>macOS 新機基礎建設：套件管理與個人 bin 的設定順序</title><link>https://tarrragon.github.io/blog/other/macos-%E6%96%B0%E6%A9%9F%E5%9F%BA%E7%A4%8E%E5%BB%BA%E8%A8%AD%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E8%88%87%E5%80%8B%E4%BA%BA-bin-%E7%9A%84%E8%A8%AD%E5%AE%9A%E9%A0%86%E5%BA%8F/</link><pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/other/macos-%E6%96%B0%E6%A9%9F%E5%9F%BA%E7%A4%8E%E5%BB%BA%E8%A8%AD%E5%A5%97%E4%BB%B6%E7%AE%A1%E7%90%86%E8%88%87%E5%80%8B%E4%BA%BA-bin-%E7%9A%84%E8%A8%AD%E5%AE%9A%E9%A0%86%E5%BA%8F/</guid><description>&lt;p>重灌或換機後要補的設定很多，但有個底層順序不能跳：套件管理工具要先到位，後面的補強才裝得起來。這篇聚焦最底層的三項基礎建設（Homebrew、bash、個人 bin），按依賴順序排列。重點不只是「裝什麼」，而是「為什麼這個順序」；之後遇到的新需求會接在後面繼續增補。&lt;/p>
&lt;h2 id="先裝-homebrew它是後面一切的基礎">先裝 Homebrew，它是後面一切的基礎&lt;/h2>
&lt;p>macOS 本身沒有內建的第三方套件管理工具，而後面幾乎每一項補強（命令列工具、開發語言、甚至部分 GUI App）都靠它安裝。沒有 Homebrew，這份清單的其他項目全部無從裝起，所以它排第一。&lt;/p>
&lt;p>安裝過程會要求輸入登入密碼（sudo），並自動下載 Xcode Command Line Tools，畫面可能停住數分鐘屬正常。&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">/bin/bash -c &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&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>裝完還要把 Homebrew 的執行檔目錄加進 PATH，shell 才找得到 &lt;code>brew&lt;/code> 與之後用它裝的工具。Homebrew 的安裝前綴依晶片而異：Apple Silicon 機器裝在 &lt;code>/opt/homebrew&lt;/code>、Intel 機器裝在 &lt;code>/usr/local&lt;/code>。安裝腳本結尾會印出對應這台機器的設定指令，照它印的路徑寫進 &lt;code>~/.zprofile&lt;/code> 讓每次開 shell 都生效。以下以 Apple Silicon 為例，Intel 機器把前綴換成 &lt;code>/usr/local&lt;/code> 即可：&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="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;eval &amp;#34;$(/opt/homebrew/bin/brew shellenv)&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zprofile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">eval&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="k">$(&lt;/span>/opt/homebrew/bin/brew shellenv&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>這一步做完，驗證安裝成功：&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">brew --version &lt;span class="c1"># 應印出 Homebrew 4.x.x&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>版本號印出來，&lt;a href="https://tarrragon.github.io/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew&lt;/a> 就能當後面所有項目的安裝來源。&lt;/p>
&lt;h2 id="把-bash-更新到-5x">把 bash 更新到 5.x&lt;/h2>
&lt;p>bash 是裝完 Homebrew 後最值得優先換掉的內建工具。macOS 的 &lt;code>/bin/bash&lt;/code> 長年凍結在 3.2 系列（2006 年釋出，目前是 patchlevel 57），Apple 不再更新它，原因是 bash 4 改用 GPLv3 授權、Apple 不願隨系統散布。對寫腳本的人來說，這代表 associative array、&lt;code>${var,,}&lt;/code> 大小寫轉換、&lt;code>mapfile&lt;/code> 等近二十年的語法都用不了。&lt;/p>
&lt;p>正確做法是用 Homebrew 另外裝一份新版並排存在，而不是覆寫系統版。&lt;code>/bin/bash&lt;/code> 在唯讀的系統卷上、受 SIP（System Integrity Protection）保護，覆寫會被擋下：&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">brew install bash&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這會把 bash 5.x 裝到 &lt;code>/opt/homebrew/bin/bash&lt;/code>，完全不碰 &lt;code>/bin/bash&lt;/code>。因為前一步已經把 &lt;code>/opt/homebrew/bin&lt;/code> 排在 PATH 前面，用 &lt;code>#!/usr/bin/env bash&lt;/code> 起手的腳本就會自動改用新版。裝完驗證一下版本確實切過去：&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">env bash --version &lt;span class="c1"># 應顯示 5.x&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">/bin/bash --version &lt;span class="c1"># 系統版仍是 3.2.57，沒被動&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>要留意的是互動 shell 在現代 macOS 預設是 zsh，這一步不影響它。更新 bash 的目的是給 &lt;code>#!/usr/bin/env bash&lt;/code> 腳本一個現代執行環境，不是換登入 shell。真要把新版 bash 當登入 shell，才需要額外把它加進 &lt;code>/etc/shells&lt;/code> 再 &lt;code>chsh&lt;/code>。&lt;/p>
&lt;h2 id="把-localbin-加進-path放個人腳本">把 ~/.local/bin 加進 PATH，放個人腳本&lt;/h2>
&lt;p>跟專案無關的小工具（例如 &lt;a href="../macos_disk_space_diagnosis/">disk-report&lt;/a> 與 &lt;a href="../macos_app_footprint_report/">app-report&lt;/a> 這類系統診斷腳本）需要一個能在任何地方直接呼叫、又不污染專案 repo 的家。慣例是個人的 &lt;code>~/.local/bin&lt;/code>，把它建好並掛上 PATH。&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">mkdir -p ~/.local/bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;export PATH=&amp;#34;$HOME/.local/bin:$PATH&amp;#34;&amp;#39;&lt;/span> &amp;gt;&amp;gt; ~/.zprofile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&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;/code>&lt;/pre>&lt;/div>&lt;p>目錄建好、PATH 掛上後，確認它確實生效：&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="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$PATH&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;:&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> grep &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$HOME&lt;/span>&lt;span class="s2">/.local/bin&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>之後把腳本 symlink 進這個目錄就能直接當指令用。&lt;/p>
&lt;h2 id="後續項目">後續項目&lt;/h2>
&lt;p>基礎建設到位後，第一個掛上去的實用腳本就是系統診斷：&lt;a href="../macos_disk_space_diagnosis/">磁碟空間診斷的 disk-report&lt;/a> 與 &lt;a href="../macos_app_footprint_report/">按 App 聚合佔用的 app-report&lt;/a>，兩支都 symlink 進 &lt;code>~/.local/bin&lt;/code> 直接當指令用。&lt;/p>
&lt;p>這份清單會隨著之後遇到的需求往下增補，新項目接在這裡。原則維持不變：基礎建設排前面，依賴它的補強排後面，每一項都寫清楚「為什麼要做」而不只是貼指令。&lt;/p></description><content:encoded><![CDATA[<p>重灌或換機後要補的設定很多，但有個底層順序不能跳：套件管理工具要先到位，後面的補強才裝得起來。這篇聚焦最底層的三項基礎建設（Homebrew、bash、個人 bin），按依賴順序排列。重點不只是「裝什麼」，而是「為什麼這個順序」；之後遇到的新需求會接在後面繼續增補。</p>
<h2 id="先裝-homebrew它是後面一切的基礎">先裝 Homebrew，它是後面一切的基礎</h2>
<p>macOS 本身沒有內建的第三方套件管理工具，而後面幾乎每一項補強（命令列工具、開發語言、甚至部分 GUI App）都靠它安裝。沒有 Homebrew，這份清單的其他項目全部無從裝起，所以它排第一。</p>
<p>安裝過程會要求輸入登入密碼（sudo），並自動下載 Xcode Command Line Tools，畫面可能停住數分鐘屬正常。</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">/bin/bash -c <span class="s2">&#34;</span><span class="k">$(</span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="k">)</span><span class="s2">&#34;</span></span></span></code></pre></div><p>裝完還要把 Homebrew 的執行檔目錄加進 PATH，shell 才找得到 <code>brew</code> 與之後用它裝的工具。Homebrew 的安裝前綴依晶片而異：Apple Silicon 機器裝在 <code>/opt/homebrew</code>、Intel 機器裝在 <code>/usr/local</code>。安裝腳本結尾會印出對應這台機器的設定指令，照它印的路徑寫進 <code>~/.zprofile</code> 讓每次開 shell 都生效。以下以 Apple Silicon 為例，Intel 機器把前綴換成 <code>/usr/local</code> 即可：</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="nb">echo</span> <span class="s1">&#39;eval &#34;$(/opt/homebrew/bin/brew shellenv)&#34;&#39;</span> &gt;&gt; ~/.zprofile
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">eval</span> <span class="s2">&#34;</span><span class="k">$(</span>/opt/homebrew/bin/brew shellenv<span class="k">)</span><span class="s2">&#34;</span></span></span></code></pre></div><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">brew --version   <span class="c1"># 應印出 Homebrew 4.x.x</span></span></span></code></pre></div><p>版本號印出來，<a href="/blog/llm/knowledge-cards/homebrew/" data-link-title="Homebrew" data-link-desc="macOS 上社群維護的套件管理器、用一行指令安裝 CLI 工具與背景服務">Homebrew</a> 就能當後面所有項目的安裝來源。</p>
<h2 id="把-bash-更新到-5x">把 bash 更新到 5.x</h2>
<p>bash 是裝完 Homebrew 後最值得優先換掉的內建工具。macOS 的 <code>/bin/bash</code> 長年凍結在 3.2 系列（2006 年釋出，目前是 patchlevel 57），Apple 不再更新它，原因是 bash 4 改用 GPLv3 授權、Apple 不願隨系統散布。對寫腳本的人來說，這代表 associative array、<code>${var,,}</code> 大小寫轉換、<code>mapfile</code> 等近二十年的語法都用不了。</p>
<p>正確做法是用 Homebrew 另外裝一份新版並排存在，而不是覆寫系統版。<code>/bin/bash</code> 在唯讀的系統卷上、受 SIP（System Integrity Protection）保護，覆寫會被擋下：</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">brew install bash</span></span></code></pre></div><p>這會把 bash 5.x 裝到 <code>/opt/homebrew/bin/bash</code>，完全不碰 <code>/bin/bash</code>。因為前一步已經把 <code>/opt/homebrew/bin</code> 排在 PATH 前面，用 <code>#!/usr/bin/env bash</code> 起手的腳本就會自動改用新版。裝完驗證一下版本確實切過去：</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">env bash --version   <span class="c1"># 應顯示 5.x</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">/bin/bash --version  <span class="c1"># 系統版仍是 3.2.57，沒被動</span></span></span></code></pre></div><p>要留意的是互動 shell 在現代 macOS 預設是 zsh，這一步不影響它。更新 bash 的目的是給 <code>#!/usr/bin/env bash</code> 腳本一個現代執行環境，不是換登入 shell。真要把新版 bash 當登入 shell，才需要額外把它加進 <code>/etc/shells</code> 再 <code>chsh</code>。</p>
<h2 id="把-localbin-加進-path放個人腳本">把 ~/.local/bin 加進 PATH，放個人腳本</h2>
<p>跟專案無關的小工具（例如 <a href="../macos_disk_space_diagnosis/">disk-report</a> 與 <a href="../macos_app_footprint_report/">app-report</a> 這類系統診斷腳本）需要一個能在任何地方直接呼叫、又不污染專案 repo 的家。慣例是個人的 <code>~/.local/bin</code>，把它建好並掛上 PATH。</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">mkdir -p ~/.local/bin
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;export PATH=&#34;$HOME/.local/bin:$PATH&#34;&#39;</span> &gt;&gt; ~/.zprofile
</span></span><span class="line"><span class="ln">3</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></code></pre></div><p>目錄建好、PATH 掛上後，確認它確實生效：</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="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$PATH</span><span class="s2">&#34;</span> <span class="p">|</span> tr <span class="s1">&#39;:&#39;</span> <span class="s1">&#39;\n&#39;</span> <span class="p">|</span> grep <span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.local/bin&#34;</span></span></span></code></pre></div><p>之後把腳本 symlink 進這個目錄就能直接當指令用。</p>
<h2 id="後續項目">後續項目</h2>
<p>基礎建設到位後，第一個掛上去的實用腳本就是系統診斷：<a href="../macos_disk_space_diagnosis/">磁碟空間診斷的 disk-report</a> 與 <a href="../macos_app_footprint_report/">按 App 聚合佔用的 app-report</a>，兩支都 symlink 進 <code>~/.local/bin</code> 直接當指令用。</p>
<p>這份清單會隨著之後遇到的需求往下增補，新項目接在這裡。原則維持不變：基礎建設排前面，依賴它的補強排後面，每一項都寫清楚「為什麼要做」而不只是貼指令。</p>
]]></content:encoded></item></channel></rss>