

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Max的程式語言筆記</title>
	<atom:link href="https://stackoverflow.max-everyday.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://stackoverflow.max-everyday.com</link>
	<description>我要當一個豬頭，快樂過每一天</description>
	<lastBuildDate>Tue, 07 Apr 2026 04:45:48 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/max-stackoverflow-256.png</url>
	<title>Max的程式語言筆記</title>
	<link>https://stackoverflow.max-everyday.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>在 Azure 中，不同 VNet 之間可以互相連線?</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/azure-vnet-peering/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/azure-vnet-peering/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 07 Apr 2026 04:45:47 +0000</pubDate>
				<category><![CDATA[Azure 筆記]]></category>
		<category><![CDATA[azure]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8255</guid>

					<description><![CDATA[在 Azure 中，不同 VNet 之間可以互相...]]></description>
										<content:encoded><![CDATA[
<p>在 Azure 中，不同 VNet 之間可以互相連線，但有幾個前提條件和不同的實作方式。</p>



<p>這正連接著你之前的問題：該用 <strong>VNet Peering</strong> 還是 <strong>Private Link</strong>？</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">1. 不同的 VNet 互連：主要方式</h3>



<h4 class="wp-block-heading"><strong>A. VNet Peering (虛擬網路對等互連) —— 最推薦</strong></h4>



<p>這是將兩個 VNet 打通最直接、效能最高的方式。</p>



<ul class="wp-block-list">
<li><strong>同區域 (Regional Peering)</strong>：連接同一個 Azure 區域內的 VNet。</li>



<li><strong>跨區域 (Global Peering)</strong>：連接不同 Azure 區域（例如：台灣與美國）的 VNet。</li>



<li><strong>特點</strong>：流量走的是 Microsoft 的私有骨幹網路，不會經過公用網際網路。連線後，兩邊的資源（如 VM）就像在同一個網路一樣，可以用私有 IP 直接互傳資料。</li>
</ul>



<h4 class="wp-block-heading"><strong>B. Private Link (私有連結)</strong></h4>



<p>如果你不想「打通整個網路」，只想讓 VNet A 的某台機器存取 VNet B 裡的「某個特定服務」（例如資料庫或你自己寫的 API），就用 Private Link。</p>



<ul class="wp-block-list">
<li><strong>優點</strong>：安全性最高，因為你只開放了「一個服務」而不是「整個網段」。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">2. <strong>關鍵限制：IP 位址不能重疊</strong></h3>



<p>在你提供的 Microsoft FAQ 連結中，最重要的一點是：<strong>如果兩個 VNet 的 IP 位址範圍 (Address Space) 有重疊，它們就「無法」透過 VNet Peering 進行連線。</strong></p>



<ul class="wp-block-list">
<li><strong>錯誤範例</strong>：
<ul class="wp-block-list">
<li>VNet A: <code>10.0.0.0/16</code></li>



<li>VNet B: <code>10.0.0.0/16</code></li>



<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>無法建立 Peering</strong>，因為路由器不知道 <code>10.0.0.1</code> 到底是指 A 還是 B。</li>
</ul>
</li>



<li><strong>正確範例</strong>：
<ul class="wp-block-list">
<li>VNet A: <code>10.1.0.0/16</code></li>



<li>VNet B: <code>10.2.0.0/16</code></li>



<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>可以完美連線</strong>。</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3. 如果 IP 真的重疊了怎麼辦？</h3>



<p>如果你接手了別人的專案，發現兩個 VNet 的 IP 範圍一模一樣，但又必須互連，這時你有兩種選擇：</p>



<ol start="1" class="wp-block-list">
<li><strong>使用 Private Link</strong>：Private Link <strong>不受 IP 重疊限制</strong>。因為它是在你的 VNet 裡產生一個新的 IP（端點），這個 IP 是你自己定的，不會衝突。</li>



<li><strong>使用 VPN Gateway (NAT)</strong>：透過 VPN 建立連線，並利用網路位址轉換 (NAT) 來避開衝突（複雜度較高）。</li>
</ol>



<h3 class="wp-block-heading">總結建議</h3>



<ul class="wp-block-list">
<li><strong>如果 IP 沒有重疊</strong>：請直接用 <strong>VNet Peering</strong>，這最簡單且速度最快。</li>



<li><strong>如果只想開放特定服務，或 IP 已重疊</strong>：請使用 <strong>Private Link</strong>。</li>



<li><strong>如果是跨專案的大型架構</strong>：建議採用上題提到的 <strong>Hub-Spoke</strong> 架構，統一規劃 IP 網段避免未來衝突。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>這組設定 <strong>絕對可以連線</strong>。</p>



<p>原因很簡單：<strong>這兩個 VNet 的 IP 位址範圍完全沒有重疊。</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">1. 網路範圍分析</h3>



<p>我們來拆解這兩個網段：</p>



<ul class="wp-block-list">
<li><strong>VNet A (<code>10.0.1.0/24</code>)</strong>：
<ul class="wp-block-list">
<li>IP 範圍：<code>10.0.1.0</code> 到 <code>10.0.1.255</code></li>



<li>總可用位址：256 個（扣除 Azure 預留的 5 個，可用 251 個）。</li>
</ul>
</li>



<li><strong>VNet B (<code>10.2.0.0/16</code>)</strong>：
<ul class="wp-block-list">
<li>IP 範圍：<code>10.2.0.0</code> 到 <code>10.2.255.255</code></li>



<li>總可用位址：65,536 個。</li>
</ul>
</li>
</ul>



<p>因為 <code>10.0.x.x</code> 與 <code>10.2.x.x</code> 是不同的區塊，所以它們在路由上不會產生衝突。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">2. 你該如何連通它們？</h3>



<p>針對你的需求，有兩種推薦做法：</p>



<h4 class="wp-block-heading"><strong>方案 A：VNet Peering（最推薦）</strong></h4>



<p>如果你希望 VNet A 的 VM 可以像在同一個辦公室一樣，直接透過 <code>10.2.0.5</code> 存取 VNet B 的機器。</p>



<ul class="wp-block-list">
<li><strong>優點</strong>：低延遲、高頻寬，設定最簡單。</li>



<li><strong>操作</strong>：在 VNet A 的設定頁面點擊「對等互連 (Peerings)」，並新增連線指向 VNet B（反向連線通常會自動建立）。</li>
</ul>



<h4 class="wp-block-heading"><strong>方案 B：Private Link（針對特定服務）</strong></h4>



<p>如果你只想讓 VNet A 存取 VNet B 裡的「特定資料庫」或「特定的 Load Balancer」，而不希望整個網路互通。</p>



<ul class="wp-block-list">
<li><strong>優點</strong>：安全性更高，符合最小權限原則。</li>



<li><strong>操作</strong>：在 VNet A 的子網段中建立一個 <strong>Private Endpoint</strong>，指向 VNet B 中的服務。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3. 注意事項（避坑指南）</h3>



<p>雖然這兩個 VNet 可以連線，但如果你之後要在這兩個 VNet 之間建立連線，請注意：</p>



<ol start="1" class="wp-block-list">
<li><strong>子網段 (Subnet) 規劃</strong>：確保你在 VNet 內部切分的 Subnet 也沒有超出上述範圍。</li>



<li><strong>DNS 解析</strong>：連線打通後，IP 雖然通了，但如果你想用「電腦名稱」存取（例如 <code>vm-prodb.internal</code>），你還需要設定 <strong>Azure Private DNS Zone</strong> 並關聯到這兩個 VNet。</li>



<li><strong>NSG 規則</strong>：記得在網路安全性群組 (NSG) 中放行來自對方網段的流量，否則封包會被防火牆擋住。</li>
</ol>



<p><strong>簡單總結：這兩個網段分得很開，你可以放心地進行 VNet Peering。</strong></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/azure-vnet-peering/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>azure Hub-Spoke（中樞與分支） 架構</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/azure-hub-spoke/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/azure-hub-spoke/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 07 Apr 2026 03:31:54 +0000</pubDate>
				<category><![CDATA[Azure 筆記]]></category>
		<category><![CDATA[azure]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8252</guid>

					<description><![CDATA[Hub-Spoke（中樞與分支） 它就像一個放射...]]></description>
										<content:encoded><![CDATA[
<p><strong>Hub-Spoke（中樞與分支）</strong> 它就像一個<strong>放射狀的交通樞紐</strong>：所有核心服務集中在「中樞」，而各個業務單位則在各自的「分支」中運行。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">1. 核心組件 (Components)</h2>



<h3 class="wp-block-heading"><strong>Hub (中樞 VNet)</strong></h3>



<p>這是網路的中央控制點，通常包含多個專案共用的<strong>共享服務</strong>：</p>



<ul class="wp-block-list">
<li><strong>Azure Firewall / NVA</strong>：負責檢查所有進出（東西向、南北向）的流量。</li>



<li><strong>VPN / ExpressRoute Gateway</strong>：連接公司地端機房（On-premises）。</li>



<li><strong>Azure Bastion</strong>：提供安全管理 VM 的跳板機。</li>



<li><strong>Shared Services</strong>：如 Active Directory (AD)、DNS 伺服器等。</li>
</ul>



<h3 class="wp-block-heading"><strong>Spoke (分支 VNet)</strong></h3>



<p>這是各個專案、開發環境或部門所擁有的隔離空間：</p>



<ul class="wp-block-list">
<li>每個專案擁有獨立的 VNet。</li>



<li><strong>不直接</strong>連接地端或公網。</li>



<li>透過 <strong>VNet Peering</strong> 連接到 Hub。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">2. 流量是如何流動的？</h2>



<p>Hub-Spoke 架構最強大的地方在於<strong>強制流量經過中樞</strong>（透過使用者定義路由 UDR）：</p>



<ol start="1" class="wp-block-list">
<li><strong>分支對分支 (Spoke-to-Spoke)</strong>：專案 A 要連到專案 B 時，流量會先送往 Hub 的防火牆進行過濾，確認安全後再轉發到專案 B。</li>



<li><strong>分支對地端 (Spoke-to-On-prem)</strong>：所有專案共用 Hub 裡的 VPN Gateway，不需要每個專案都買一個昂貴的網關。</li>



<li><strong>分支對公網 (Spoke-to-Internet)</strong>：統一由 Hub 的防火牆出口，方便進行稽核與流量監控。</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">3. 為什麼要用這種架構？ (優點)</h2>



<ul class="wp-block-list">
<li><strong>節省成本</strong>：避免在多個 VNet 中重複部署昂貴的資源（如防火牆和網關）。</li>



<li><strong>中央管理 (Governance)</strong>：資安團隊只需要在 Hub 守好「大門」，就能管理所有專案的進出安全。</li>



<li><strong>隔離性 (Isolation)</strong>：不同專案（Spoke）之間預設是不通的，除非你在 Hub 設定了路由。這符合「最小權限」原則。</li>



<li><strong>突破限制</strong>：單個 VNet 有資源配額上限，透過多個 Spoke 可以繞過這些限制。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4. 搭配 Private Link 的進階用法</h2>



<p>在 Hub-Spoke 環境下，<strong>Private Link</strong> 扮演了畫龍點睛的角色：</p>



<ul class="wp-block-list">
<li><strong>中央私有端點</strong>：你可以把 Private Endpoint 放在 Hub 的一個專門子網段內。</li>



<li><strong>跨分支存取</strong>：所有 Spoke 都可以透過 VNet Peering 存取 Hub 裡那個唯一的 Private IP 來連到資料庫，這樣你就不需要在每個專案裡都建立一大堆 Endpoint。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">5. 什麼時候不建議用？</h2>



<p>如果你的規模非常小（只有 1-2 個 VNet），且沒有連回地端的需求，Hub-Spoke 可能會增加過多的<strong>複雜性</strong>與<strong>維運成本</strong>。這種情況下，簡單的 VNet Peering 就足夠了。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>小撇步</strong>：如果你不想手動管理這些路由和連線，Azure 提供了一種託管服務叫 <strong>Azure Virtual WAN</strong>，它本質上就是「服務化的 Hub-Spoke」，會幫你自動處理好所有的自動路由。</p>
</blockquote>



<p>你目前的公司架構，是否已經面臨多個專案需要統一管控安全出口的問題了呢？</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/azure-hub-spoke/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>azure Private Link  與 vnet 差異</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/azure-private-link-vnet/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/azure-private-link-vnet/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 07 Apr 2026 03:27:32 +0000</pubDate>
				<category><![CDATA[Azure 筆記]]></category>
		<category><![CDATA[azure]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8250</guid>

					<description><![CDATA[簡單來說，Azure VNet 是你的「雲端辦公...]]></description>
										<content:encoded><![CDATA[
<p>簡單來說，<strong>Azure VNet</strong> 是你的「雲端辦公室（基礎建設）」，而 <strong>Azure Private Link</strong> 則是通往這個辦公室的一條「VIP 專用通道」。</p>



<p>這兩者並非競爭關係，而是<strong>互補關係</strong>。你通常是在 VNet 內部部署 Private Link。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">1. 核心概念對比</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>特性</strong></td><td><strong>Azure Virtual Network (VNet)</strong></td><td><strong>Azure Private Link</strong></td></tr></thead><tbody><tr><td><strong>本質</strong></td><td><strong>基礎設施</strong>：在 Azure 中邏輯隔離的私有網路。</td><td><strong>連線技術</strong>：將外部服務帶入 VNet 的技術。</td></tr><tr><td><strong>角色</strong></td><td>它是你部署資源（如 VM）的<strong>容器</strong>。</td><td>它是存取 PaaS（如 SQL, Storage）的<strong>路徑</strong>。</td></tr><tr><td><strong>主要功能</strong></td><td>提供 IP 位址空間、子網劃分、路由。</td><td>透過<strong>私有 IP</strong> 存取服務，完全避開公用網路。</td></tr><tr><td><strong>連線對象</strong></td><td>VM、Load Balancer、VPN/ExpressRoute。</td><td>Azure PaaS (SQL, Web App)、自建服務。</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">2. Azure VNet (虛擬網路)</h2>



<p>VNet 是雲端中的私有網路。你可以控制其 IP 位址空間、劃分子網（Subnets），並在其中運行虛擬機器（VM）。</p>



<ul class="wp-block-list">
<li><strong>內部通訊</strong>：VNet 內的 VM 預設可以互相通訊。</li>



<li><strong>對外通訊</strong>：如果你需要 VNet A 連結 VNet B，你會使用 <strong>VNet Peering</strong>。</li>



<li><strong>安全性</strong>：使用網路安全性群組 (NSG) 來過濾流量。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">3. Azure Private Link (私有連結)</h2>



<p>當你想從 VNet 內存取 Azure 的 PaaS 服務（例如 <strong>Azure SQL</strong> 或 <strong>Storage Account</strong>）時，傳統做法是透過公用 IP 存取。<strong>Private Link</strong> 改變了這一點：</p>



<ul class="wp-block-list">
<li><strong>Private Endpoint (私有端點)</strong>：它會在你的 VNet 中產生一個<strong>虛擬網卡 (NIC)</strong>，並分配一個你 VNet 範圍內的<strong>私有 IP</strong>。</li>



<li><strong>安全隔離</strong>：你的流量會透過 Microsoft 的骨幹網路傳輸，<strong>絕不會經過公用網際網路</strong>。</li>



<li><strong>防止資料外洩</strong>：因為該私有端點只對應到特定的服務實例（例如：某個特定的資料庫），而不是整個雲端服務區塊，這大幅降低了資料被非法導向外部的風險。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">4. 什麼時候該用哪一個？</h2>



<ul class="wp-block-list">
<li><strong>如果你要建置網路環境</strong>：你需要 <strong>VNet</strong>。它是所有網路操作的基礎。</li>



<li><strong>如果你要連接兩組 VM 網路</strong>：使用 <strong>VNet Peering</strong>（VNet 對等互連）。</li>



<li><strong>如果你要從 VNet 安全地存取資料庫或儲存體</strong>：使用 <strong>Private Link</strong>。</li>



<li><strong>如果你是服務提供者</strong>：你想把自己的服務私密地分享給其他客戶，而不暴露在公網上，你會建立 <strong>Private Link Service</strong>。</li>
</ul>



<h3 class="wp-block-heading">總結一句話：</h3>



<p><strong>VNet</strong> 決定了你的資源「住在哪裡」，而 <strong>Private Link</strong> 決定了你的資源如何「不出門且安全地」與外部服務溝通。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>在 Azure 的多專案、多子網段（Subnet）環境下，這並不是「二選一」的問題，而是<strong>如何將兩者結合使用</strong>的架構設計問題。</p>



<p>針對你的情境，核心邏輯如下：</p>



<h3 class="wp-block-heading">1. 跨專案的基礎網路連線：使用 VNet Peering</h3>



<p>如果你的「不同專案」位於不同的 VNet 中，且你希望這些專案內部的虛擬機器（VM）或服務能夠互相溝通，你首先需要解決的是<strong>網路打通</strong>的問題。</p>



<ul class="wp-block-list">
<li><strong>解決方案</strong>：<strong>VNet Peering（虛擬網路對等互連）</strong>。</li>



<li><strong>做法</strong>：將專案 A 的 VNet 與專案 B 的 VNet 建立 Peering。</li>



<li><strong>結果</strong>：兩個 VNet 就像在同一個網路內一樣，可以透過私有 IP 互通。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">2. 跨專案存取 PaaS 服務：使用 Private Link</h3>



<p>如果你的情境是：專案 A 有一個 VM，需要存取專案 B 裡面的 <strong>Azure SQL 資料庫</strong>或 <strong>Storage Account</strong>，且你不希望透過公用網路。</p>



<ul class="wp-block-list">
<li><strong>解決方案</strong>：<strong>Azure Private Link (Private Endpoint)</strong>。</li>



<li><strong>做法</strong>：
<ol start="1" class="wp-block-list">
<li>在專案 B 的資料庫上啟用 Private Link。</li>



<li>在專案 A 的 VNet/Subnet 中建立一個 <strong>Private Endpoint（私有端點）</strong>。</li>
</ol>
</li>



<li><strong>結果</strong>：專案 A 的子網段會多出一個私有 IP，專案 A 的 VM 只要存取這個 IP，就能連到專案 B 的資料庫，完全不需要 VNet Peering 也能運作。</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">3. 決策指南：我該選哪一個？</h3>



<p>請根據你的<strong>通訊對象</strong>來決定：</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>你的需求場景</strong></td><td><strong>推薦方案</strong></td></tr></thead><tbody><tr><td><strong>兩個專案的 VM 之間要互傳資料</strong></td><td><strong>VNet Peering</strong> (建立大範圍的網路連線)</td></tr><tr><td><strong>從專案 A 存取專案 B 的資料庫 (PaaS)</strong></td><td><strong>Private Link</strong> (最安全，針對單一服務)</td></tr><tr><td><strong>不同專案共用同一個防火牆 (Hub-Spoke)</strong></td><td><strong>VNet Peering</strong> (將各專案連至中心 VNet)</td></tr><tr><td><strong>安全性要求極高，禁止任何公網存取</strong></td><td><strong>Private Link</strong> (確保資料存取路徑唯一且私有)</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">4. 實務建議：Hub-Spoke 架構</h3>



<p>在企業環境中，通常會採用 <strong>Hub-Spoke（中樞與分支）</strong> 架構：</p>



<ol start="1" class="wp-block-list">
<li><strong>Hub VNet</strong>：放置防火牆（Azure Firewall）或 VPN Gateway。</li>



<li><strong>Spoke VNet (各專案)</strong>：每個專案擁有自己的 VNet 和子網段，並透過 <strong>VNet Peering</strong> 連接到 Hub。</li>



<li><strong>Private Link</strong>：各專案需要存取資料庫時，在各自的子網段內建立 <strong>Private Endpoint</strong>。</li>
</ol>



<h3 class="wp-block-heading">總結</h3>



<ul class="wp-block-list">
<li>如果你要連通的是「整個網路環境」，請用 <strong>VNet Peering</strong>。</li>



<li>如果你要存取的是「特定的 PaaS 服務（如 SQL, Web App, Storage）」，請用 <strong>Private Link</strong>。</li>
</ul>



<p>在跨專案的情況下，通常會先用 <strong>VNet Peering</strong> 把網路架構搭起來，再針對敏感的資料庫使用 <strong>Private Link</strong> 來加強安全。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/azure-private-link-vnet/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>github CodeQL 支援 PHP 嗎?</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/codeql-%e6%94%af%e6%8f%b4-php-%e5%97%8e/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/codeql-%e6%94%af%e6%8f%b4-php-%e5%97%8e/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 07 Apr 2026 00:11:51 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8246</guid>

					<description><![CDATA[error message: Error: Di...]]></description>
										<content:encoded><![CDATA[
<p>error message:</p>



<pre class="wp-block-preformatted">Error: Did not recognize the following languages: php<br>Warning: This run of the CodeQL Action does not have permission to access the CodeQL Action API endpoints. This could be because the Action is running on a pull request from a fork. If not, please ensure the workflow has at least the 'security-events: read' permission. Details: Code scanning is not enabled for this repository. Please enable code scanning in the repository settings. - https://docs.github.com/rest</pre>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="976" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/chrome_2026-04-07-08-04-4n-1024x976.jpg?v=1775520285" alt="" class="wp-image-8247" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/chrome_2026-04-07-08-04-4n-1024x976.jpg?v=1775520285 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/chrome_2026-04-07-08-04-4n-600x572.jpg?v=1775520285 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/chrome_2026-04-07-08-04-4n-768x732.jpg?v=1775520285 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/chrome_2026-04-07-08-04-4n-1536x1463.jpg?v=1775520285 1536w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/chrome_2026-04-07-08-04-4n.jpg?v=1775520285 1797w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>這看起來像是 CodeQL 分析時遇到了兩個常見的障礙：一個是語言設定問題，另一個是 GitHub API 的權限或設定問題。</p>



<p>以下是針對這兩項錯誤的解決方案：</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">1. 語言錯誤：<code>Did not recognize the following languages: php</code></h2>



<p><strong>問題點：</strong> 雖然 CodeQL 支援 PHP，但系統提示無法辨認。</p>



<p><strong>解決方法：</strong></p>



<ul class="wp-block-list">
<li><strong>檢查大小寫：</strong> 在 <code>.github/workflows/codeql.yml</code> 檔案中，請確保語言名稱是<strong>全小寫</strong>。</li>



<li><strong>更新 Action 版本：</strong> 確保你使用的是較新的版本（建議使用 <code>@v3</code>）。</li>



<li><strong>縮排與語法：</strong> 如果有多種語言，請使用列表格式。</li>
</ul>



<p>YAML</p>



<pre class="wp-block-code"><code># 範例設定
- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    languages: php  # 確保這裡是小寫
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">2. 權限與 API 錯誤：<code>does not have permission</code></h2>



<p><strong>問題點：</strong> 工作流（Workflow）沒有權限將掃描結果上傳到 GitHub 的安全後台。這通常發生在<strong>從 Fork（分叉）出來的 PR</strong>，或是權限設定過於嚴格時。</p>



<p><strong>解決方法：</strong></p>



<p>你需要在 YAML 檔案中明確加入 <code>permissions</code> 區塊，賦予工作流寫入安全事件的權力：</p>



<p>YAML</p>



<pre class="wp-block-code"><code>permissions:
  actions: read
  contents: read
  security-events: write  # 這是上傳結果最關鍵的權限
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">3. 警告：<code>Code scanning is not enabled</code></h2>



<p><strong>問題點：</strong> 你的儲存庫（Repository）尚未正式啟用 Code Scanning 功能。</p>



<p><strong>操作步驟：</strong></p>



<ol start="1" class="wp-block-list">
<li>進入 GitHub 儲存庫的 <strong>Settings（設定）</strong>。</li>



<li>點擊左側選單的 <strong>Code security and analysis</strong>。</li>



<li>找到 <strong>Code scanning</strong> 並點擊 <strong>Set up</strong> 或確保其為開啟狀態。</li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>[!IMPORTANT]</p>



<p>如果這是<strong>私有儲存庫（Private Repo）</strong>，你必須擁有 <strong>GitHub Advanced Security</strong> 授權才能使用 CodeQL 的自動掃描功能。</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading">快速檢查清單</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>問題類型</strong></td><td><strong>應對動作</strong></td></tr></thead><tbody><tr><td><strong>來自 Fork 的 PR</strong></td><td>到 <strong>Settings &gt; Actions &gt; General</strong> 確認是否允許 Fork 的 PR 執行工作流並存取秘密（Secrets）。</td></tr><tr><td><strong>多語言設定</strong></td><td>若有其他語言，請寫成：<code>languages: [ 'javascript', 'php' ]</code>。</td></tr><tr><td><strong>GitHub Enterprise</strong></td><td>如果你在企業版環境，請確認 Runner 是否可以連線到 GitHub 的 API 端點。</td></tr></tbody></table></figure>



<p>請問你這個專案是公開的還是組織內的私有專案呢？這會影響到權限設定的細節。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>討論串: Why doesn&#8217;t CodeQL support auditing PHP<br><a href="https://github.com/github/codeql/issues/12376">https://github.com/github/codeql/issues/12376</a></p>



<p>這是一個很常見的誤解。簡單來說：<strong>CodeQL 絕對支援 PHP</strong>，但在 GitHub Action 的某些特定版本或環境下，可能會因為「配置方式」導致它找不到 PHP。</p>



<p>關於你提到的 issue (#12376) 以及相關的討論，這裡有幾個核心重點幫助你理解：</p>



<h3 class="wp-block-heading">1. CodeQL 確實支援 PHP</h3>



<p>PHP 是 CodeQL 官方支援的語言之一（包括分析 SQL 注入、跨站腳本 XSS 等漏洞）。你可以從官方文件中看到 PHP 始終在支援清單中。</p>



<h3 class="wp-block-heading">2. 為什麼會出現 &#8220;Did not recognize php&#8221; 的錯誤？</h3>



<p>在該 issue 和類似案例中，通常是因為以下兩個原因：</p>



<ul class="wp-block-list">
<li><strong>版本相容性 (Action Version)：</strong> 如果你使用的 <code>github/codeql-action</code> 版本太舊，或者內含的 CodeQL CLI 版本不匹配，可能會導致語言包加載失敗。</li>



<li><strong>預設套件包 (Standard Bundles)：</strong>CodeQL 在執行時會下載一個包含所有語言規則的「套件包 (Bundle)」。如果下載過程中出錯，或是你指定的分析器路徑不對，就會報出找不到語言的錯誤。</li>



<li><strong>大小寫問題：</strong>CodeQL 對設定檔非常敏感，必須確保是全小寫的 <code>php</code>。</li>
</ul>



<h3 class="wp-block-heading">3. 如何解決這個問題？</h3>



<p>根據目前的最佳實踐，請檢查並修正你的 <code>.github/workflows/codeql.yml</code>：</p>



<h4 class="wp-block-heading">A. 更新 Action 版本（最重要）</h4>



<p>確保你使用的是 <code>v3</code>（目前的主流版本）：</p>



<p>YAML</p>



<pre class="wp-block-code"><code>- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    languages: php
</code></pre>



<h4 class="wp-block-heading">B. 手動指定 CodeQL 套件來源（如果問題持續）</h4>



<p>有時候 GitHub 的託管執行器（Runner）快取了壞掉的 CodeQL 版本。你可以嘗試強制它使用最新的版本：</p>



<p>YAML</p>



<pre class="wp-block-code"><code>- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    languages: php
    tools: latest # 強制下載最新版工具
</code></pre>



<h3 class="wp-block-heading">4. 針對你提到的權限錯誤（補充）</h3>



<p>你之前提到的第二個錯誤 <code>Warning: This run of the CodeQL Action does not have permission...</code> 其實才是主因。</p>



<p>當 CodeQL 因為<strong>權限不足</strong>無法從 GitHub API 獲取配置或下載必要的語言組件時，它可能會退回到一個「空狀態」，然後報錯說它「不認識 php」。</p>



<p><strong>請務必檢查：</strong></p>



<ul class="wp-block-list">
<li>你的 Repository 設置中：<strong>Settings > Code security and analysis > Code scanning</strong> 是否已啟動？</li>



<li>如果這是 <strong>Fork</strong> 過來的專案，預設是不能執行 CodeQL 並回傳結果的（為了安全）。你必須在 PR 頁面手動核准執行，或者在該 Fork 倉庫的設定中開啟 Actions 權限。</li>
</ul>



<h3 class="wp-block-heading">總結</h3>



<p><strong>CodeQL 支援 PHP。</strong> 你看到的錯誤通常不是因為「不支援」，而是因為 <strong>權限被阻擋</strong> 或 <strong>Action 版本過舊</strong> 導致 CodeQL 無法正確加載 PHP 的分析模組。請嘗試將 Action 更新至 <code>v3</code> 並在 YAML 中加入 <code>permissions: security-events: write</code>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/codeql-%e6%94%af%e6%8f%b4-php-%e5%97%8e/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>zi2zi 訓練圖片的參數調整, 嚴重影響 infer 結果</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/zi2zi-font2image-offset/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/zi2zi-font2image-offset/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Sat, 04 Apr 2026 08:01:55 +0000</pubDate>
				<category><![CDATA[機器學習筆記]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8239</guid>

					<description><![CDATA[已經學習了一段時間的訓練資料才發現正確答案應該往...]]></description>
										<content:encoded><![CDATA[
<p>已經學習了一段時間的訓練資料才發現正確答案應該往上偏移 2px, 這個應該 infer 結果再 offset 回來就好了, 我試著把 offset 的資料拿去訓練了一個晚上.</p>



<p>訓練結果慘烈, 會有白色細小線條.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="768" height="384" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/20518.png?v=1775280961" alt="" class="wp-image-8238" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/20518.png?v=1775280961 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/20518-600x300.png?v=1775280961 600w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>程式碼:<br><a href="https://github.com/max32002/zi2zi-pytorch">https://github.com/max32002/zi2zi-pytorch</a></p>



<p>出問題的 script</p>



<pre class="wp-block-code"><code>rd /q/s C:\Max\Documents\zi2zi-pytorch\source\paired_images-maruko-bold-384-jp
python font2img.py --src_font=source/font/SweiGothicCJKjp-Bold.ttf ^
    --dst_font=source/font/ZenMaruGothic-Bold.ttf ^
    --charset=charset/charset_maruko_train_match_noto_bold.txt ^
    --sample_dir=source/paired_images-maruko-bold-384-jp ^
    --shuffle ^
    --canvas_size=384 ^
    --char_size=384 ^
    --dst_y_offset=-111 ^
    --disable_auto_fit ^
    --mode=font2font</code></pre>



<p>應該要使用 -109 offset, but 不小心讓已訓練的模型在 -111</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>解法:<br><a href="https://github.com/max32002/zi2zi-pytorch/commit/ff8bf15893eab1c22118c9ab61fbce00b38a7a04">https://github.com/max32002/zi2zi-pytorch/commit/ff8bf15893eab1c22118c9ab61fbce00b38a7a04</a></p>



<pre class="wp-block-code"><code>            if output_x_offset != 0 or output_y_offset != 0:
                h, w = processed_image.shape&#91;:2]
                M = np.float32(&#91;&#91;1, 0, output_x_offset], &#91;0, 1, output_y_offset]])
                processed_image = cv2.warpAffine(processed_image, M, (w, h),
                                                 borderMode=cv2.BORDER_CONSTANT, borderValue=255)</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/zi2zi-font2image-offset/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>當 AI 把你的文章複製貼上三次：一個讓人又笑又哭的 Gemini CLI 偵錯紀錄</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/ai-paste-multi-times-gemini-cli/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/ai-paste-multi-times-gemini-cli/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 02 Apr 2026 05:48:39 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8232</guid>

					<description><![CDATA[為什麼 Skill Mode 下 keyword...]]></description>
										<content:encoded><![CDATA[
<p>為什麼 Skill Mode 下 keyword.md 會重複寫入？三個根本原因、三個修法，還有一些讓你少掉幾根頭髮的教訓。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4cb.png" alt="📋" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 本文目錄</h2>



<ol class="wp-block-list">
<li><a href="#intro">事情是這樣開始的</a></li>



<li><a href="#what-happened">發生了什麼事：文章被複製三次</a></li>



<li><a href="#root-causes">根本原因大揭秘（共三個，一個比一個妙）</a></li>



<li><a href="#fix">怎麼修：三個外科手術等級的改法</a></li>



<li><a href="#lesson">血淚教訓：寫 AI Prompt 規格時你一定要知道的事</a></li>



<li><a href="#tldr">懶人包 TL;DR</a></li>
</ol>



<h2 class="wp-block-heading" id="intro">事情是這樣開始的</h2>



<p>想像一下你打開了一個剛生成的&nbsp;<code>keyword.md</code>，然後你看到這個：</p>



<pre class="wp-block-code"><code># 遠端工作的真實面貌

遠端工作聽起來很美好，穿著睡衣開會、貓咪在旁邊陪...

（此處省略 1200 字精彩文章）

---

# 遠端工作的真實面貌

遠端工作聽起來很美好，穿著睡衣開會、貓咪在旁邊陪...

（此處又省略 1200 字一模一樣的文章）

---

# 遠端工作的真實面貌

（你沒看錯，還有第三份）</code></pre>



<p>恭喜你，你的 AI 助理今天特別勤勞，把同一篇文章幫你寫了三遍。 這不是功能，這是 Bug。而且是那種「看起來好像在工作」的 Bug， 最讓人崩潰。</p>



<p>這是我們在開發&nbsp;<strong>blog-pro-max</strong>（一套把 Skill 注入到 18 種 AI 平台的部落格寫作工具）時， 在 Gemini CLI 的 Skill Mode 上踩到的真實坑。這篇文章就是完整的偵錯過程紀錄， 希望你看完之後，可以省下我花掉的那幾個小時。</p>



<h2 class="wp-block-heading" id="what-happened">發生了什麼事：文章被複製三次</h2>



<h3 class="wp-block-heading">系統架構快速說明</h3>



<p>blog-pro-max 的運作方式是這樣的：</p>



<ol class="wp-block-list">
<li>用 <code>blogpro init --ai gemini</code> 把一個 <strong>SKILL.md</strong> 注入到 Gemini CLI</li>



<li>Gemini CLI 讀取 SKILL.md，知道自己有「寫部落格文章」的能力</li>



<li>使用者輸入指令，Gemini CLI 依照 SKILL.md 的規格執行：生成文章、做分析、存檔</li>
</ol>



<p>理論上，最終應該輸出三個乾淨的檔案：</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th class="has-text-align-left" data-align="left">檔案</th><th class="has-text-align-left" data-align="left">內容</th></tr><tr><td><code>output/keyword.md</code></td><td>文章本文（乾淨，只有文章）</td></tr><tr><td><code>output/keyword_analysis.md</code></td><td>所有分析報告（標題建議、審稿、趨勢⋯⋯）</td></tr><tr><td><code>output/keyword.html</code></td><td>兩者合併的完整 HTML，一頁看完</td></tr></tbody></table></figure>



<p>實際上，<code>keyword.md</code>&nbsp;裡面長這樣：</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 實際輸出（節錄）</p>



<p>文章本文 × 1 → 邏輯審稿報告（包含再次輸出的文章本文）× 1 → 結構審稿報告（又包含文章本文）× 1 → &#8230;</p>



<p><strong>最後 keyword.md 的大小：應有 3KB，實際有 27KB。</strong></p>



<h2 class="wp-block-heading" id="root-causes">根本原因大揭秘（共三個，一個比一個妙）</h2>



<p>找 Bug 的第一步，當然是把程式碼放在眼前盯著看，然後說「這不可能啊」。 讓我們來逐一拆解。</p>



<p><strong>Bug #1</strong></p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> LLM 每產一段分析就 write_file 一次</p>



<p>SKILL.md 的 LLM-only 模式指示 Gemini CLI：「執行全科檢查、標題建議、封面提示詞、時事趨勢……」 沒有明確說什麼時候存檔。於是 Gemini CLI 很有 initiative 地選擇：&nbsp;<strong>每產出一段分析，就立刻呼叫 write_file 存一次。</strong></p>



<p>問題是：LLM 在呼叫 write_file 的時候，通常不是「附加模式（append）」， 而是<strong>重新生成整個檔案內容</strong>。它會把整個對話脈絡（包含剛才輸出的文章本文） 一起寫進去。然後下一段分析完成，又重新寫一次，文章本文又出現一次。</p>



<p>執行 12 段分析 = 文章本文出現 13 次（第一次正常存，後面 12 次每次都夾帶一份）。 恭喜你，獲得免費的 ×13 文章。</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 核心觀念</p>



<p>LLM 的 write_file 工具呼叫是<strong>覆寫（overwrite）</strong>，不是 append。 每次呼叫都會寫入你在 prompt context 裡看到的「目前完整內容」。 你以為在附加，AI 其實在複製貼上。</p>



<p><strong>Bug #2</strong></p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92f.png" alt="🤯" class="wp-smiley" style="height: 1em; max-height: 1em;" /> LLM-only 區塊裡偷偷藏了腳本指令（邏輯矛盾）</p>



<p>這個 Bug 讓我看了三遍才看懂。在 SKILL.md 的「<strong>不具備執行腳本能力</strong>」區塊裡， 步驟 5 有這樣一行：</p>



<pre class="wp-block-code"><code>- 若 AI 具備執行腳本能力，執行：
    python output_md2html.py output/keyword.md output/keyword.html \
      --analysis output/keyword_analysis.md</code></pre>



<p>等等……這在「<em>不</em>具備腳本能力」的區塊裡，說的是「若<em>具備</em>腳本能力」？？</p>



<p>這是某次重構時的遺留程式碼（我知道，不要笑）。 Gemini CLI 解讀這個邏輯的方式是： 「我是 Gemini CLI，我可以執行腳本，所以我要走腳本路徑。 但我現在也在 LLM 模式裡，所以我也要走 LLM 路徑。」 於是兩條路都走了，<strong>存了兩次</strong>。</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 這是什麼感覺</p>



<p>就像告示牌上寫：「如果你會開車，請停車。如果你不會開車，請停車。如果你會開車，請繼續開。」 然後有個人在十字路口停了三次。</p>



<p><strong>Bug #3</strong></p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1faa4.png" alt="🪤" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 腳本執行失敗時靜默回退到 LLM 模式</p>



<p><code>content_research.py</code>&nbsp;需要&nbsp;<code>GITHUB_TOKEN</code>&nbsp;或&nbsp;<code>OPENAI_API_KEY</code>&nbsp;才能呼叫 LLM。如果這些 key 沒設定，腳本會直接 crash。</p>



<p>但 SKILL.md 沒有說腳本失敗時要怎麼辦。於是 Gemini CLI 很有 can-do 精神地說： 「腳本不行沒關係，我自己用 LLM 生成！」</p>



<p>然後就悲劇了：腳本已經把文章部分寫入了&nbsp;<code>keyword.md</code>（或者創建了空檔案）， Gemini CLI 的 LLM 又重新生成一遍，疊加進去。 最後你打開&nbsp;<code>keyword.md</code>，看到的是腳本的殘骸 + LLM 的全文，拼在一起。</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 沉默的失敗是最可怕的</p>



<p>明確的錯誤訊息是你的朋友。靜默回退是你最危險的敵人。 永遠要明確指定「失敗時應該做什麼」，尤其是當執行結果會寫入檔案的時候。</p>



<h2 class="wp-block-heading" id="fix">怎麼修：三個外科手術等級的改法</h2>



<p>找到病因之後，修法其實相當直接。以下是每個 Bug 對應的修法。</p>



<p><strong>Fix #1</strong></p>



<p>明確指定「全部完成後一次性寫入」</p>



<p>在 SKILL.md 的 LLM-only 存檔規則前，加入明確的防護說明：</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 舊的（含糊）</p>



<p>自動執行所有分析：全科檢查、標題建議……</p>



<p>儲存規則：<br>&#8211; output/keyword.md：僅存文章本文<br>&#8211; output/keyword_analysis.md：所有分析結果<br>&#8211; output/keyword.html：合併</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 新的（明確）</p>



<p>執行順序（所有分析在記憶體中累積）：<br>全科檢查 → 標題 → 封面 → 時事趨勢 → &#8230;</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 存檔規則（必須嚴格遵守）：<br>&#8211; keyword.md：只呼叫 write_file 一次，之後絕對不再修改<br>&#8211; keyword_analysis.md：全部完成後才 write_file 一次<br>&#8211;&nbsp;<strong>禁止對同一個檔案呼叫多次 write_file</strong></p>



<p>關鍵字是「<strong>記憶體中累積</strong>」和「<strong>一次性寫入</strong>」。 給 LLM 的指令越明確，它越不會自由發揮。</p>



<p><strong>Fix #2</strong></p>



<p>把錯誤嵌套的腳本指令移出 LLM-only 區塊</p>



<p>把那行混入 LLM-only 區塊的&nbsp;<code>output_md2html.py</code>&nbsp;指令直接刪除。 LLM-only 模式就讓 LLM 自己生成 HTML，不要夾帶腳本指令。</p>



<p>改完之後，兩個區塊變得清清楚楚、互不干擾：</p>



<pre class="wp-block-code"><code>/* 具備腳本能力 */
→ 執行 content_research.py（腳本自己搞定一切）
→ 回報結果

/* 不具備腳本能力（純 LLM） */
→ 生成文章 + 執行所有分析（全在 memory）
→ 一次性寫三個檔案
→ HTML 直接用 LLM 合併兩個 .md 的內容輸出</code></pre>



<p><strong>Fix #3</strong></p>



<p>在腳本模式加「失敗就停，別偷偷改模式」</p>



<p>在「具備腳本能力」的步驟 5 後面，加上明確的失敗處理指令：</p>



<pre class="wp-block-code"><code>&gt; &#x26a0; 若腳本執行失敗（例如缺少 API 金鑰或環境問題），
&gt; 請直接顯示錯誤訊息並停止。
&gt; 不要改用 LLM 模式重新生成文章，
&gt; 以避免與腳本已寫入的部分內容重疊。</code></pre>



<p>就這一段話。十秒鐘加進去的文字，省了好幾次「為什麼文章又重複了」的困惑。</p>



<h2 class="wp-block-heading" id="lesson">血淚教訓：寫 AI Prompt 規格時你一定要知道的事</h2>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Lesson 1：LLM 的 write_file ≠ append</h3>



<p>這是最根本的認知差距。你可能以為 LLM 在「附加分析到 .md 檔」， 但它實際上是「重新生成完整的 .md 內容後覆寫」。&nbsp;<strong>在任何涉及分次寫入的場景，都必須明確指定「collect → write once」的順序。</strong></p>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Lesson 2：Prompt 的分支邏輯要互斥且完整</h3>



<p>if A → do X，if not A → do Y。這兩個分支要確保：</p>



<ul class="wp-block-list">
<li>互斥（進了 A 就不能再進 not-A 的分支）</li>



<li>每個分支裡只有屬於自己的指令（不要偷渡對方的內容）</li>



<li>都有失敗處理（明確說失敗時要做什麼，不要讓 AI 自由心證）</li>
</ul>



<p>人寫 if/else 都會 bug，更何況是自然語言寫給 LLM 的條件分支。</p>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Lesson 3：靜默回退（Silent Fallback）是所有 Bug 的老爸</h3>



<p>在 AI agent 系統裡，「我做不到就換個方式做」聽起來很聰明， 但如果沒有明確說「換個方式前要先清除之前的影響」， 就會出現兩個執行路徑同時留下痕跡的混亂狀態。</p>



<p><strong>規則很簡單：可以 fallback，但必須先 cleanup；或者根本禁止 fallback，直接 fail-fast。</strong>&nbsp;後者在寫入檔案的場景通常更安全。</p>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Lesson 4：Prompt Spec 也要 Code Review</h3>



<p>SKILL.md 裡的那個「LLM-only 區塊藏著腳本指令」的 Bug， 是重構時從其他地方複製過來的殘留程式碼。</p>



<p>普通程式碼有 linter、有 type checker、有 unit test。 Prompt 規格文件什麼都沒有。 所以在你覺得「這個 prompt 寫好了」的時候，最好把它當成 code 一樣做一次&nbsp;<strong>邏輯審查</strong>：每個分支都走一遍，看看有沒有矛盾的指令。</p>



<h3 class="wp-block-heading"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Lesson 5：「應該很直觀」是 Bug 最常見的藏身地</h3>



<p>「LLM 應該知道只要寫一次吧？」 「腳本失敗了應該會跳錯吧？」 「分支應該不會混在一起吧？」</p>



<p>以上三句話，分別對應了本文的三個 Bug。 凡是「應該」的地方，就是你需要寫明確指令的地方。 在人機協作的系統裡，不要假設任何「顯而易見」的行為。</p>



<h2 class="wp-block-heading" id="tldr">懶人包 TL;DR</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th class="has-text-align-left" data-align="left">Bug</th><th class="has-text-align-left" data-align="left">根本原因</th><th class="has-text-align-left" data-align="left">修法</th></tr><tr><td>文章本文重複 N 次</td><td>LLM 每段分析後各自 write_file，覆寫時夾帶完整 context</td><td>明確指定「全部在記憶體累積，最後一次性 write_file」</td></tr><tr><td>同一份檔案被存兩次</td><td>LLM-only 區塊裡偷藏了腳本指令，兩條路都走</td><td>移除嵌套的錯誤指令，確保分支互斥</td></tr><tr><td>腳本 + LLM 輸出疊加</td><td>腳本失敗時靜默回退 LLM 模式，兩者輸出重疊</td><td>加入「腳本失敗停止，禁止靜默回退」明確指令</td></tr></tbody></table></figure>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f389.png" alt="🎉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 修完之後的感覺</p>



<p>打開&nbsp;<code>keyword.md</code>，看到乾淨的文章本文，只有文章本文，沒有重複， 分析全部在&nbsp;<code>keyword_analysis.md</code>，HTML 合併得漂漂亮亮。 這種「終於正常了」的快感，大概是所有開發者共同的語言。</p>



<p>如果你也在開發類似的 AI agent 系統，希望這篇文章能幫你少踩幾個坑。 如果你踩到的坑比我還奇特，歡迎留言分享，讓大家一起開心（或一起哭）。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/ai-paste-multi-times-gemini-cli/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Xcopy 存取遭到拒絕解決方案</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/xcopy-git-fail/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/xcopy-git-fail/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 01 Apr 2026 10:34:23 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8226</guid>

					<description><![CDATA[這是在執行 xcopy 指令時，因為嘗試複製 ....]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="420" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/WindowsTerminal_2026-04-01-18-32-4m-1024x420.jpg?v=1775039604" alt="" class="wp-image-8227" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/WindowsTerminal_2026-04-01-18-32-4m-1024x420.jpg?v=1775039604 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/WindowsTerminal_2026-04-01-18-32-4m-600x246.jpg?v=1775039604 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/WindowsTerminal_2026-04-01-18-32-4m-768x315.jpg?v=1775039604 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/WindowsTerminal_2026-04-01-18-32-4m.jpg?v=1775039604 1169w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>這是在執行 xcopy 指令時，因為嘗試複製 .git 資料夾內的系統檔案或正在被使用的檔案，導致權限不足或檔案被鎖定而中斷。當你使用 /s 參數時，xcopy 會連同隱藏的設定檔一起搬移，但 Git 的內部資料庫通常不建議這樣直接複製。</p>



<p>解決這個問題最快的方法是排除 .git 資料夾，因為你通常只需要程式碼檔案，而不需要把舊的 Git 紀錄也強制蓋過去。你可以改用 robocopy 指令，它比 xcopy 更強大，且可以輕鬆排除特定目錄。</p>



<p>請嘗試執行以下指令：</p>



<pre class="wp-block-code"><code>robocopy "c:\max\play\blog-pro-max" "c:\max\git\blog-pro-max" /s /e /xd .git
</code></pre>



<p>這個指令的原理是將來源路徑的所有子目錄與檔案複製到目標路徑，但透過 /xd 參數把 .git 資料夾排除在外。這樣就能避免觸碰那些會導致存取被拒的隱藏系統檔案，同時確保你的程式碼與文件能順利完成同步。</p>



<p>如果你堅持要連同 Git 紀錄一起複製，請先確認沒有任何程式（例如 VS Code 或 Git GUI）正在開啟該專案，並嘗試以系統管理員身分執行命令提示字元。不過實務上，使用 robocopy 排除不必要的元資料是比較穩健的做法。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/xcopy-git-fail/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>git push 每次都要輸入密碼鎖(passphrase)的解法</title>
		<link>https://stackoverflow.max-everyday.com/2026/04/git-push-passphrase/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/04/git-push-passphrase/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 01 Apr 2026 09:17:05 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8219</guid>

					<description><![CDATA[之前產生的 SSH 金鑰有設定密碼鎖。雖然這比沒...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="559" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/git-push-passphrase_clean-1024x559.jpg?v=1775035163" alt="" class="wp-image-8222" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/git-push-passphrase_clean-1024x559.jpg?v=1775035163 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/git-push-passphrase_clean-600x327.jpg?v=1775035163 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/git-push-passphrase_clean-768x419.jpg?v=1775035163 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/git-push-passphrase_clean.jpg?v=1775035163 1408w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>之前產生的 SSH 金鑰有設定密碼鎖。雖然這比沒設密碼安全，但每次都要輸入確實很麻煩。如果你不想重新產生金鑰，有兩個方法可以解決。</p>



<h2 class="wp-block-heading">使用 ssh-agent 自動管理</h2>



<p>這是最推薦的做法。你可以讓系統在背景跑一個代理程式，幫你記住解鎖後的金鑰。只要在 WSL 終端機執行這兩行指令：</p>



<pre class="wp-block-code"><code>eval $(ssh-agent -s)
ssh-add ~/.ssh/id_rsa_personal</code></pre>



<p>執行第二行時，輸入最後一次密碼。之後只要這個視窗沒關掉，再次 push 就不會再問你密碼。</p>



<p>如果你希望每次開啟 WSL 都自動生效，可以把這兩行指令加到 ~/.bashrc 檔案的最末端。這樣每天第一次開機輸入一次，之後整天都不用再打。</p>



<h2 class="wp-block-heading">移除金鑰本身的密碼</h2>



<p>如果你覺得在私有電腦上不需要這層保護，也可以直接把金鑰的密碼拿掉。執行這行指令：</p>



<pre class="wp-block-code"><code>ssh-keygen -p -f ~/.ssh/id_rsa_personal</code></pre>



<p>系統會先要求你輸入舊密碼。接著會問你新的密碼，這時直接按兩次 Enter 留白。這樣這組金鑰就會變成無密碼狀態，以後執行 git push 就會直接通過。</p>



<h2 class="wp-block-heading">確認設定是否正確</h2>



<p>處理完密碼後，你可以測試一下連線是否正常。輸入：</p>



<pre class="wp-block-code"><code>ssh -T git@github.com -i ~/.ssh/id_rsa_personal</code></pre>



<p>如果看到 Hi 帳號名！You&#8217;ve successfully authenticated，就代表搞定了。以後不管 Windows 那邊登入哪個帳號，WSL 這邊都會走你這組專屬的 SSH 金鑰。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>如果你使用的是 zsh，要把設定寫在 <strong>~/.zshrc</strong> 這個檔案裡。</p>



<h2 class="wp-block-heading">修改設定檔</h2>



<p>你可以使用 nano 或 vim 來編輯：</p>



<pre class="wp-block-code"><code>nano ~/.zshrc</code></pre>



<p>把下面這段程式碼貼到檔案的最末端：</p>



<p>Bash</p>



<pre class="wp-block-code"><code>if &#91; -z "$SSH_AUTH_SOCK" ]; then
   eval $(ssh-agent -s)
   ssh-add ~/.ssh/id_rsa_personal
fi
</code></pre>



<p>存檔離開後，執行 <strong>source ~/.zshrc</strong> 讓設定立即生效。這樣你下次開啟新的 zsh 視窗時，系統就會自動啟動代理並載入你的金鑰。</p>



<h2 class="wp-block-heading">使用 oh-my-zsh 插件</h2>



<p>如果你有安裝 oh-my-zsh，其實有一個更優雅的插件做法。在 ~/.zshrc 找到 <strong>plugins=(&#8230;)</strong> 這行，把 <strong>ssh-agent</strong> 加進去：</p>



<pre class="wp-block-code"><code>plugins=(git ssh-agent)</code></pre>



<p>接著在下面補上一行設定，指定要自動載入的金鑰名稱：</p>



<pre class="wp-block-code"><code>zstyle :omni:plugins:ssh-agent identities id_rsa_personal</code></pre>



<p>這樣它會在你開啟終端機時自動處理一切，不用自己寫 shell script。</p>



<h2 class="wp-block-heading">解決重複啟動問題</h2>



<p>要注意的是，如果直接在設定檔寫 eval，每次開新視窗都會產生一個新的 ssh-agent 處理程序。如果你發現系統變慢，或是用 ps 指令看到一堆 ssh-agent，建議使用上面提到的 if 判斷式，或是安裝 <strong>keychain</strong> 這個套件來管理。</p>



<p>keychain 會幫你找現有的代理程序，確保整個系統只跑一個 ssh-agent。這對於習慣開多個分頁的人來說，是比較乾淨的做法。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">關於 Keychain</h2>



<p>keychain 是一個用來管理 ssh-agent 的工具。它的好處是能讓所有開啟的終端機視窗共用同一個 ssh-agent。如果不使用它，你每開一個新的 zsh 分頁，系統可能就會跑一個新的 ssh-agent 程式，久了會變得很亂。</p>



<p>keychain 會在背景記住你解鎖過的金鑰。只要你在第一次登入時輸入過一次密碼，之後不管是重開分頁還是執行 git push，它都不會再煩你。</p>



<h2 class="wp-block-heading">安裝方式</h2>



<p>在 Ubuntu 或 WSL (Ubuntu) 環境下，安裝非常簡單。直接輸入這行指令：</p>



<pre class="wp-block-code"><code>sudo apt update &amp;&amp; sudo apt install keychain</code></pre>



<p>安裝完後，它還不會自動運作，需要去修改你的 zsh 設定檔。</p>



<h2 class="wp-block-heading">設定 zsh 自動執行</h2>



<p>你需要把啟動指令加進 <strong>~/.zshrc</strong>。請編輯該檔案：</p>



<p>nano ~/.zshrc</p>



<p>在檔案最後面加上這兩行程式碼：</p>



<p>Bash</p>



<pre class="wp-block-code"><code>/usr/bin/keychain --nogui ~/.ssh/id_rsa_personal
source ~/.keychain/$HOST-sh
</code></pre>



<p>第一行是叫 keychain 去管理你的那把金鑰。第二行則是載入相關的環境變數，讓目前的 zsh 知道要去哪裡找已經解鎖的金鑰。</p>



<h2 class="wp-block-heading">實際使用效果</h2>



<p>設定完成後，執行 <strong>source ~/.zshrc</strong> 或是重開視窗。這時候系統會最後一次詢問你金鑰的密碼。</p>



<p>輸入完畢後，這把金鑰就會一直保持在解鎖狀態。就算你開了十個 zsh 視窗，也都只需要輸入那一次密碼。這對頻繁使用 git push 的開發者來說，是最省心也兼顧安全的方法。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>在 Windows CMD 環境下，Git 每次 push 都要求輸入密碼鎖，通常是因為 SSH Agent 沒有在背景執行，或是金鑰沒有被正確載入。以下提供兩個主要的解決方案。</p>



<h3 class="wp-block-heading">使用 Windows 內建的 OpenSSH 服務</h3>



<p>這是最推薦的做法，因為它可以讓 SSH Agent 在開機時自動啟動，並持久化儲存你的金鑰密碼。</p>



<p>以管理員身分開啟 PowerShell，執行以下指令將 OpenSSH Authentication Agent 服務設定為自動啟動</p>



<pre class="wp-block-code"><code>Set-Service -Name ssh-agent -StartupType AutomaticStart-Service ssh-agent</code></pre>



<p>回到一般的 CMD 視窗，將你的私鑰加入 Agent。假設你的金鑰路徑是預設值：</p>



<pre class="wp-block-code"><code>ssh-add %USERPROFILE%\.ssh\id_rsa</code></pre>



<p>執行後系統會要求你輸入最後一次密碼鎖。之後只要服務維持執行狀態，重開 CMD 也不需要再輸入密碼。</p>



<p>執行上述指令, 錯誤訊息:</p>



<pre class="wp-block-preformatted">Error connecting to agent: No such file or directory</pre>



<p>這個錯誤通常是因為 CMD 無法識別 %USERPROFILE% 後接續的路徑格式，或是 ssh-agent 服務雖然啟動了，但目前的視窗環境變數還沒有對接上。你可以依照以下步驟排查並修正。</p>



<h3 class="wp-block-heading">修正路徑格式與環境變數</h3>



<p>在 CMD 中，路徑的分隔符號與變數引用非常嚴格。請嘗試手動補上反斜線，並加上雙引號確保路徑正確：</p>



<pre class="wp-block-code"><code>ssh-add "%USERPROFILE%\.ssh\id_rsa"</code></pre>



<p>如果依然顯示無法連線到 Agent，請在同一個 CMD 視窗內執行以下指令，手動啟動 Agent 並設定環境變數：</p>



<pre class="wp-block-code"><code>for /f "tokens=1-2 delims==;" %a in ('ssh-agent -s') do set %a=%b</code></pre>



<p>執行完畢後，再次嘗試執行 ssh-add 指令。</p>



<h3 class="wp-block-heading">確認 SSH 服務執行狀態</h3>



<p>如果手動啟動有效，但每次開新視窗都要重來，代表系統服務沒有正確常駐。請再次確認服務狀態：</p>



<ol start="1" class="wp-block-list">
<li>按下 Win + R 鍵，輸入 services.msc 並執行。</li>



<li>找到 OpenSSH Authentication Agent。</li>



<li>確認狀態為 正在執行，且啟動類型為 自動。</li>
</ol>



<p>結果是服務被停用, 啟用就好了.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="590" height="710" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/2026-04-02_11-51_n3.jpg?v=1775101951" alt="" class="wp-image-8230" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/2026-04-02_11-51_n3.jpg?v=1775101951 590w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/04/2026-04-02_11-51_n3-499x600.jpg?v=1775101951 499w" sizes="auto, (max-width: 590px) 100vw, 590px" /></figure>



<h3 class="wp-block-heading">檢查 GIT_SSH 環境變數</h3>



<p>有時候安裝 Git 時會內建自家的 SSH 套件，這會干擾系統內建的 ssh-add。請檢查你的環境變數中是否有名為 GIT_SSH 的變數。如果有，請將它刪除，讓 Git 預設使用系統路徑 C:\Windows\System32\OpenSSH\ssh.exe。</p>



<p>另外，請確認你的 .ssh 資料夾內確實存在 id_rsa 這個檔案。你可以輸入 dir %USERPROFILE%.ssh 來查看檔案清單。如果你的金鑰檔名不是 id_rsa (例如是 id_ed25519)，請將指令中的檔名更換為正確的名稱。</p>



<h3 class="wp-block-heading">設定 Git 使用內建的 SSH 工具</h3>



<p>有時候 Git 會使用自己打包的 SSH 工具而不是 Windows 系統路徑下的工具，這會導致兩者金鑰紀錄不互通。你可以強制 Git 指向系統的 SSH 路徑：</p>



<pre class="wp-block-code"><code>git config --global core.sshCommand C:/Windows/System32/OpenSSH/ssh.exe</code></pre>



<h3 class="wp-block-heading">修改金鑰設定移除密碼鎖</h3>



<p>如果你覺得維護 Agent 服務太麻煩，且確認電腦環境安全，也可以選擇直接移除私鑰的密碼鎖。</p>



<p>執行以下指令：</p>



<pre class="wp-block-code"><code>ssh-keygen -p -f %USERPROFILE%.ssh\id_rsa</code></pre>



<p>系統會先要求輸入舊密碼，當詢問新密碼時直接按兩次 Enter 留白。這樣該金鑰就會變成無密碼狀態，之後任何 Git 操作都不會再跳出輸入提示。不過請注意，這會降低金鑰外洩時的安全性。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/04/git-push-passphrase/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>用 AI 修改原作者 theme改版</title>
		<link>https://stackoverflow.max-everyday.com/2026/03/patch_gutenshop-py/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/03/patch_gutenshop-py/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 31 Mar 2026 05:02:58 +0000</pubDate>
				<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[WordPress筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[wordpress]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8211</guid>

					<description><![CDATA[希望可以把改動的部份寫成一個 python sc...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="558" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/WindowsTerminal_2026-03-31-13-02-4g-1024x558.jpg" alt="" class="wp-image-8212" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/WindowsTerminal_2026-03-31-13-02-4g-1024x558.jpg?v=1774933354 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/WindowsTerminal_2026-03-31-13-02-4g-600x327.jpg?v=1774933354 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/WindowsTerminal_2026-03-31-13-02-4g-768x419.jpg?v=1774933354 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/WindowsTerminal_2026-03-31-13-02-4g-1536x837.jpg?v=1774933354 1536w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/WindowsTerminal_2026-03-31-13-02-4g.jpg?v=1774933354 1970w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>希望可以把改動的部份寫成一個 python script 給我進行 patch.</p>



<p>目前的設定是 max-width: 75rem; (1200px)，若扣除內距或特定的瀏覽器渲染，體感寬度確實接近 1150px。要提升到1280px，我們需要將其改為 80rem (1280 / 16 = 780)。</p>



<p>這是一個進階版的 Python 腳本，它會自動掃描伺服器路徑（如 /var/www/），尋找所有名為 gutenshop 的主題目錄，並統一對其<br>foundation.css 進行寬度補丁。<br><br>✦ 我現在就在當前目錄為您產生 patch_gutenshop.py 實體檔案</p>



<pre class="wp-block-code"><code>import os
import re
import sys

def patch_gutenshop_width(base_path='.'):
    """
    自動尋找並修改所有 gutenshop 主題的寬度限制。
    目標寬度: 1280px (80rem)
    """
    # 目標檔案在主題內的相對路徑
    target_rel_path = os.path.join('css', 'foundation.css')
    found_count = 0
    patched_count = 0

    print(f"開始掃描目錄: {os.path.abspath(base_path)}")
    print("-" * 50)

    # 遍歷目錄尋找所有名為 gutenshop 的主題目錄
    for root, dirs, files in os.walk(base_path):
        if os.path.basename(root) == 'gutenshop':
            full_file_path = os.path.join(root, target_rel_path)
            
            if os.path.exists(full_file_path):
                found_count += 1
                print(f"找到目標: {full_file_path}")
                
                try:
                    with open(full_file_path, 'r', encoding='utf-8') as f:
                        content = f.read()

                    # 將 75rem (1200px) 替換為 80rem (1280px)
                    # 使用正則表達式鎖定 max-width: 75rem
                    new_content = re.sub(r'max-width:\s*75rem', 'max-width: 80rem', content)

                    if content == new_content:
                        # 檢查是否已經是 80rem，避免誤報
                        if 'max-width: 80rem' in content:
                            print("  &#91;跳過] 寬度已經是 1280px (80rem)")
                        else:
                            print("  &#91;跳過] 檔案中未找到 max-width: 75rem 設定")
                    else:
                        with open(full_file_path, 'w', encoding='utf-8') as f:
                            f.write(new_content)
                        patched_count += 1
                        print("  &#91;成功] 已將寬度修正為 1280px (80rem)")
                
                except Exception as e:
                    print(f"  &#91;錯誤] 無法讀寫檔案: {e}")
            
    print("-" * 50)
    print(f"掃描結束。共找到 {found_count} 個主題，成功補丁 {patched_count} 個檔案。")

if __name__ == "__main__":
    # 如果執行時有帶參數，則以參數為搜尋路徑，否則預設為當前路徑 '.'
    search_dir = sys.argv&#91;1] if len(sys.argv) > 1 else '.'
    
    # Linux 權限提示
    if os.name != 'nt' and hasattr(os, 'getuid') and os.getuid() != 0:
        print("提示：在 Linux 伺服器上建議使用 sudo 權限執行以確保檔案可寫入。")
        
    patch_gutenshop_width(search_dir)
</code></pre>



<p>使用方式：</p>



<p>本機測試：直接執行 python patch_gutenshop.py，它會掃描當前目錄。</p>



<p>伺服器大量修改：將此檔案上傳至伺服器，並指定掃描路徑：</p>



<pre class="wp-block-code"><code>sudo python3 patch_gutenshop.py /var/www </code></pre>



<p>此腳本會尋找所有路徑下包含 gutenshop 的目錄，並修改其 css/foundation.css 中的 max-width: 75rem 為 max-width: 80rem (1280px)。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/03/patch_gutenshop-py/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>5 分鐘學會 SSH Config：輕鬆管理你的所有伺服器金鑰</title>
		<link>https://stackoverflow.max-everyday.com/2026/03/manage-ssh-keys-with-config/</link>
					<comments>https://stackoverflow.max-everyday.com/2026/03/manage-ssh-keys-with-config/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 31 Mar 2026 04:25:02 +0000</pubDate>
				<category><![CDATA[電腦相關應用]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=8208</guid>

					<description><![CDATA[很多人為了方便管理，會把預設的 SSH 私鑰檔名...]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="572" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/manage-ssh-keys-with-config-1024x572.jpg?v=1774931085" alt="" class="wp-image-8209" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/manage-ssh-keys-with-config-1024x572.jpg?v=1774931085 1024w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/manage-ssh-keys-with-config-600x335.jpg?v=1774931085 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/manage-ssh-keys-with-config-768x429.jpg?v=1774931085 768w, https://stackoverflow.max-everyday.com/wp-content/uploads/2026/03/manage-ssh-keys-with-config.jpg?v=1774931085 1376w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>很多人為了方便管理，會把預設的 SSH 私鑰檔名從&nbsp;<code>id_rsa</code>&nbsp;改成更有辨識度的名稱，例如&nbsp;<code>id_rsa_personal</code>。沒想到改完名後，原本可以秒連的伺服器突然都跳出權限錯誤。這不是因為金鑰失效，而是因為 SSH 客戶端「迷路」了。</p>



<h2 class="wp-block-heading">為什麼改名後會連線失敗？</h2>



<p>SSH 客戶端在發起連線時，預設只會自動尋找幾個固定名稱的檔案，像是&nbsp;<code>id_rsa</code>、<code>id_ecdsa</code>&nbsp;或&nbsp;<code>id_ed25519</code>。一旦你將檔案改名，原本依賴自動偵測的機制就會找不到對應的私鑰，導致連線被拒絕。</p>



<p>要修復這個問題，你不需要把檔名改回去，而是要學會使用 SSH 的「地圖」——也就是&nbsp;<code>config</code>&nbsp;檔案。</p>



<h2 class="wp-block-heading">使用 Config 建立通用對應規則</h2>



<p>你可以透過編輯&nbsp;<code>~/.ssh/config</code>&nbsp;檔案，告訴 SSH 客戶端你的金鑰躲在哪裡。請在該檔案中加入以下設定：</p>



<pre class="wp-block-code"><code>Host *
  IdentityFile ~/.ssh/id_rsa_personal
</code></pre>



<p>這段設定的意思是：無論連線到哪一個網域（Host *），如果找不到特定的金鑰，就統一嘗試使用這把&nbsp;<code>id_rsa_personal</code>。這樣一來，原本受影響的站點就能恢復正常連線。</p>



<p>舉例：這就像你在公司設定了一個「預設聯絡人」，不管客戶從哪裡打來，只要沒人接，電話就會自動轉給這個人。</p>



<h2 class="wp-block-heading">多把金鑰的精確分流做法</h2>



<p>如果你手邊有多把金鑰分別對應不同的帳號（例如公司用與私人用），建議採取更精確的分流設定。例如，針對 GitHub 使用特定的金鑰，而其他站點則使用另一把：</p>



<pre class="wp-block-code"><code>Host github.com
HostName github.com
IdentityFile ~/.ssh/id_rsa_personal

Host *
IdentityFile ~/.ssh/id_rsa_other</code></pre>



<p>透過這種方式，SSH 就會根據你連線的目標，自動選取正確的「鑰匙」開門，再也不用手動指定。</p>



<h2 class="wp-block-heading">連線診斷與最後提醒</h2>



<p>設定好之後，你可以使用&nbsp;<code>ssh -v</code>&nbsp;加上目標網域（例如&nbsp;<code>ssh -v git@github.com</code>）來觀察連線過程。在輸出的冗長資訊中，你會看到 SSH 開始嘗試載入你指定的&nbsp;<code>IdentityFile</code>。</p>



<p>在 Linux 或 WSL 的環境下，系統對金鑰檔案的安全性要求很高。如果你改名後檔案的權限跑掉了，系統會覺得這把鑰匙太危險而拒絕使用。</p>



<p>請檢查你的金鑰權限是否維持在 600（這代表只有你自己可以讀寫）。你可以輸入指令檢查，如果權限不對，連線是絕對不會成功的。</p>



<p>如果你平常有在使用 ssh-agent（這是一個幫你在背景管理鑰匙、讓你不用每次都輸入密碼的小工具），光是改掉檔案名稱是不夠的。</p>



<p>因為管理員的記憶中存的還是舊的名字。改名後，請記得執行 ssh-add ~/.ssh/id_rsa_personal（這裡的檔名請換成你修改後的名字），手動將新檔名的金鑰重新載入到記憶體中，這樣連線時系統才抓得到正確的鑰匙。</p>



<h2 class="wp-block-heading">結語</h2>



<p>管理 SSH 金鑰不該是一件痛苦的事。透過&nbsp;<code>~/.ssh/config</code>&nbsp;檔案，我們不僅能自由為金鑰命名，還能優雅地處理多個帳號的分流連線。下次遇到連線問題時，先檢查一下你的「地圖」(config)設定是否正確吧！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2026/03/manage-ssh-keys-with-config/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
