如何优雅地寻找 Tesla ?

<p>近期,Tesla APP 发布了 4.24.0 版本,在 iOS 的“快捷指令”中可以直接调用部分 Tesla 命令了!借助快捷指令,你能很优雅地找到你的 Tesla !</p> <p>有时候我们将车停在了地库中的一个不常去的位置,用车时往往就找不到车。之前我经常使用 Tesla APP 上的“鸣笛”功能来加速自己找车的速度,而在近期,Tesla APP 发布了 4.24.0 版本,在 iOS 的“快捷指令”中可以直接调用部分 Tesla 命令了!这是一个非常令人兴奋的功能!因为这意味着,我们完全可以设定一个 iOS 快捷指令,直接呼唤 Siri 来寻找我们的 Tesla 。</p> <p><a href="https://www.icloud.com/shortcuts/5e5754b692b646e9a00e599a33a8dd36">安装「我的 Tesla 在哪里?」快捷指令</a></p> <p>使用方法:</p> <ul> <li>对 iPhone 说出「嘿 Siri ,我的 Tesla 在哪里?」来触发快捷指令,运行后根据提示操作即可。</li> </ul> <hr> <p>Q: 为什么快捷指令安装后无法使用?</p> <p>A: 请确保 iPhone 上的 Tesla APP 版本在 4.24.0 及以上。</p> <p>Q: 可以在 Android 设备上使用吗?</p> <p>A: 此快捷指令依赖 iOS,无法在 Android 设备上使用。</p>

2023/8/29
阅读更多

Mastering Git Cherry-pick

<p>本文希望教你如何成为一个 <code>git cherry-pick</code> 的 “master”!通过使用 <code>git cherry-pick</code> 来轻松地维护多个分支版本,再也不会让 multi-version maintaining 成为你心头上的那把令你屡次痛心的剑了!</p> <p>本文所有内容都会基于以下(精心构造的)示例,该例子涵盖了大部分工程上容易出现的 Git log pattern(如有其他 corner-case ,欢迎<a href="mailto:me@triplez.cn">联系我</a>,一起努力让该文变得对大家更有帮助)。</p> <p>贯穿本文的示例场景,这是该示例基于时间序的 Git 提交历史。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-12">12</a> </span><span class="lnt" id="hl-0-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-13">13</a> </span><span class="lnt" id="hl-0-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-14">14</a> </span><span class="lnt" id="hl-0-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-15">15</a> </span><span class="lnt" id="hl-0-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-16">16</a> </span><span class="lnt" id="hl-0-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-17">17</a> </span><span class="lnt" id="hl-0-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-18">18</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order </span></span><span class="line"><span class="cl">* f2c1619 <span class="o">(</span>HEAD -&gt; red<span class="o">)</span> R6 </span></span><span class="line"><span class="cl">* e6899ea R5 merge branch <span class="s1">&#39;blue&#39;</span> into <span class="s1">&#39;red&#39;</span> </span></span><span class="line"><span class="cl"><span class="p">|</span><span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span>* <span class="se">\ </span> 0979d45 R4 merge branch <span class="s1">&#39;green&#39;</span> into <span class="s1">&#39;red&#39;</span> </span></span><span class="line"><span class="cl"><span class="p">|</span><span class="se">\ \ </span></span></span><span class="line"><span class="cl"><span class="se"></span><span class="p">|</span> <span class="p">|</span> * 186da41 <span class="o">(</span>blue<span class="o">)</span> B3 </span></span><span class="line"><span class="cl"><span class="p">|</span> * <span class="p">|</span> c950910 <span class="o">(</span>green<span class="o">)</span> G3 </span></span><span class="line"><span class="cl">* <span class="p">|</span> <span class="p">|</span> 17e2629 R3 </span></span><span class="line"><span class="cl"><span class="p">|</span> <span class="p">|</span> * 69edfc9 B2 </span></span><span class="line"><span class="cl"><span class="p">|</span> * <span class="p">|</span> 059425a G2 </span></span><span class="line"><span class="cl"><span class="p">|</span> * <span class="p">|</span> 05719c8 G1 </span></span><span class="line"><span class="cl"><span class="p">|</span> <span class="p">|</span> * ebb218d B1 </span></span><span class="line"><span class="cl"><span class="p">|</span> <span class="p">|</span>/ </span></span><span class="line"><span class="cl">* / 8c6595b R2 </span></span><span class="line"><span class="cl"><span class="p">|</span>/ </span></span><span class="line"><span class="cl">* 6581ff8 R1 </span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit </span></span></code></pre></td></tr></table> </div> </div><details> <summary><mark>快速创建该示例。</mark></summary> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-13">13</a> </span><span class="lnt" id="hl-1-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-14">14</a> </span><span class="lnt" id="hl-1-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-15">15</a> </span><span class="lnt" id="hl-1-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-16">16</a> </span><span class="lnt" id="hl-1-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-17">17</a> </span><span class="lnt" id="hl-1-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-18">18</a> </span><span class="lnt" id="hl-1-19"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-19">19</a> </span><span class="lnt" id="hl-1-20"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-20">20</a> </span><span class="lnt" id="hl-1-21"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-21">21</a> </span><span class="lnt" id="hl-1-22"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-22">22</a> </span><span class="lnt" id="hl-1-23"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-23">23</a> </span><span class="lnt" id="hl-1-24"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-24">24</a> </span><span class="lnt" id="hl-1-25"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-25">25</a> </span><span class="lnt" id="hl-1-26"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-26">26</a> </span><span class="lnt" id="hl-1-27"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-27">27</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir cherry-pick<span class="p">;</span> <span class="nb">cd</span> cherry-pick/ </span></span><span class="line"><span class="cl">git init </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;init&#34;</span> &gt;&gt; init<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;init commit&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout -b red </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R1&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git branch green </span></span><span class="line"><span class="cl">git branch blue </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R2&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout blue </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;blue&#34;</span> &gt;&gt; blue<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;B1&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout green </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;green&#34;</span> &gt;&gt; green<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;G1&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;green&#34;</span> &gt;&gt; green<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;G2&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout blue </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;blue&#34;</span> &gt;&gt; blue<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;B2&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout red </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R3&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout green </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;green&#34;</span> &gt;&gt; green<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;G3&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout blue </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;blue&#34;</span> &gt;&gt; blue<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;B3&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git checkout red </span></span><span class="line"><span class="cl">git merge green -m <span class="s2">&#34;R4 merge branch &#39;green&#39; into &#39;red&#39;&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl">git merge blue -m <span class="s2">&#34;R5 merge branch &#39;blue&#39; into &#39;red&#39;&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;red&#34;</span> &gt;&gt; red<span class="p">;</span> git add -A<span class="p">;</span> git commit -m <span class="s2">&#34;R6&#34;</span><span class="p">;</span> sleep <span class="m">1</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">git --no-pager log --oneline --graph --date-order </span></span></code></pre></td></tr></table> </div> </div></details> <br/> <p>当前 Git 提交历史示意图如下。</p> <figure class="align-center "> <img loading="lazy" src="https://blog.triplez.cn/img/mastering-git-cherry-pick/git-cherry-pick-overall.jpg#center" alt="提交记录示意图"/> <figcaption> 提交记录示意图 </figcaption> </figure> <p>Git cherry-pick 的命令的基本原理是根据用户所选择的提交,根据提交中的差异信息(diff)将这些提交移植至用户目标版本中。如将 hotfix 应用至其他 LTS 版本中是该功能的一个典型应用。</p> <p><code>git cherry-pick</code> 的大致用法为:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git cherry-pick <span class="o">[</span>options<span class="o">]</span> &lt;commit&gt;... </span></span></code></pre></td></tr></table> </div> </div><p>此处的 <code>&lt;commit&gt;...</code> 即为用户希望移植的提交(集合),这是本文讨论的要点。</p> <p><code>&lt;commit&gt;</code> 可以为单一提交(commit),也可以为一个版本区间(revision range)。若为 revision range,则该命令会将该 revision range 中的所有 commit 都解析出来,最终成为一连串的单一 commit <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。<code>cherry-pick</code> 可以同时接受多个 <code>&lt;commit&gt;</code> ,此时表现类似于 <code>git rev-list</code> 中的 <code>--no-walk</code> 行为<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p> <p>那我们依次来讨论 <code>&lt;commit&gt;...</code> 为单一 commit 以及 revision range 的情况。</p> <h2 id="single-commit">Single commit</h2> <h3 id="normal-commit">Normal commit</h3> <p>回到上文的例子,如果仅需要将 <code>G2</code> 选取出来,我们可以这样操作。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1">1</a> </span><span class="lnt" id="hl-3-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-2">2</a> </span><span class="lnt" id="hl-3-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-3">3</a> </span><span class="lnt" id="hl-3-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-4">4</a> </span><span class="lnt" id="hl-3-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span> </span></span><span class="line"><span class="cl">$ git checkout master </span></span><span class="line"><span class="cl">$ git checkout -b cp-single-normal-commit </span></span><span class="line"><span class="cl"><span class="c1"># G2 的提交 SHA 值为 059425a</span> </span></span><span class="line"><span class="cl">$ git cherry-pick 059425a </span></span></code></pre></td></tr></table> </div> </div><p>此时会出现合并冲突(merge conflict),输出如下所示。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-1">1</a> </span><span class="lnt" id="hl-4-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-2">2</a> </span><span class="lnt" id="hl-4-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-3">3</a> </span><span class="lnt" id="hl-4-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-4">4</a> </span><span class="lnt" id="hl-4-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">CONFLICT (modify/delete): green deleted in HEAD and modified in 059425a (G2). Version 059425a (G2) of green left in tree. </span></span><span class="line"><span class="cl">error: could not apply 059425a... G2 </span></span><span class="line"><span class="cl">hint: after resolving the conflicts, mark the corrected paths </span></span><span class="line"><span class="cl">hint: with &#39;git add &lt;paths&gt;&#39; or &#39;git rm &lt;paths&gt;&#39; </span></span><span class="line"><span class="cl">hint: and commit the result with &#39;git commit&#39; </span></span></code></pre></td></tr></table> </div> </div><p>这段内容告知我们这些信息:<code>green</code> 文件在当前(暂存)版本 <code>HEAD</code> 中并不存在,但在选取的 <code>G2</code> 提交中存在。如果需要该文件,则使用 <code>git add</code> 将其提交至暂存区,若希望保留当前暂存版本的状态,即删除该文件,则使用 <code>git rm</code> 将 <code>green</code> 文件舍弃。</p> <p>我们希望在选取 G2 之后能够保留 <code>green</code> 文件,故采取如下操作。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-1">1</a> </span><span class="lnt" id="hl-5-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-2">2</a> </span><span class="lnt" id="hl-5-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-3">3</a> </span><span class="lnt" id="hl-5-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-4">4</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 将 green 提交至暂存区</span> </span></span><span class="line"><span class="cl">$ git add green </span></span><span class="line"><span class="cl"><span class="c1"># 已修复所有合并冲突,继续进行 cherry-pick</span> </span></span><span class="line"><span class="cl">$ git cherry-pick --continue </span></span></code></pre></td></tr></table> </div> </div><p>此时 cherry-pick 操作已经完成,如果继续执行 <code>git cherry-pick --continue</code> ,则此时会显示 <code>error: no cherry-pick or revert in progress</code> ,即当前没有进行任何 cherry-pick 任务。</p> <p>查看一下当前的提交记录,则会发现 <code>G2</code> 已经在我们当前的分支 <code>cp-single-normal-commit</code> 上了。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-1">1</a> </span><span class="lnt" id="hl-6-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-2">2</a> </span><span class="lnt" id="hl-6-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-3">3</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order </span></span><span class="line"><span class="cl">* <span class="m">0457362</span> <span class="o">(</span>HEAD -&gt; cp-single-normal-commit<span class="o">)</span> G2 </span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit </span></span></code></pre></td></tr></table> </div> </div><h3 id="merge-commit">Merge commit</h3> <p>那如果我们想选取一个 merge commit 呢,比如将 <code>R4</code> 选取出来。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-7-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-1">1</a> </span><span class="lnt" id="hl-7-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-2">2</a> </span><span class="lnt" id="hl-7-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-3">3</a> </span><span class="lnt" id="hl-7-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-4">4</a> </span><span class="lnt" id="hl-7-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span> </span></span><span class="line"><span class="cl">$ git checkout master </span></span><span class="line"><span class="cl">$ git checkout -b cp-single-merge-commit </span></span><span class="line"><span class="cl"><span class="c1"># R4 的提交 SHA 值为 0979d45</span> </span></span><span class="line"><span class="cl">$ git cherry-pick 0979d45 </span></span></code></pre></td></tr></table> </div> </div><p>当执行完这条 <code>cherry-pick</code> 命令之后,你会得到以下输出。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-8-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-1">1</a> </span><span class="lnt" id="hl-8-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">error: commit 0979d45f1b46f72730188c5c01b3f2c7f41b18e6 is a merge but no -m option was given. </span></span><span class="line"><span class="cl">fatal: cherry-pick failed </span></span></code></pre></td></tr></table> </div> </div><p>默认情况下,<code>cherry-pick</code> 不处理 merge commit 并直接报错。因为在 merge commit 中,会有多个 parent 信息,但此时 Git 并不知道该使用哪个 parent 作为 mainline。在错误信息中,也同时提示了我们,如果要选取 merge commit ,则需要使用 <code>-m</code> (亦为 <code>--mainline</code>)选项来指定哪个 parent 是主线<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p> <p>通过 <code>git show</code> 命令可以获得 merge commit 的多个 parent,且从 1 开始编号。由于该例中我们需要选取的 mainline parent 是 <code>R3(17e2629)</code> ,因此在 <code>cherry-pick</code> 中选择的是 <code>-m 1</code>。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-9-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-1">1</a> </span><span class="lnt" id="hl-9-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-2">2</a> </span><span class="lnt" id="hl-9-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-3">3</a> </span><span class="lnt" id="hl-9-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-4">4</a> </span><span class="lnt" id="hl-9-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-5">5</a> </span><span class="lnt" id="hl-9-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-6">6</a> </span><span class="lnt" id="hl-9-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-9-7">7</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager show 0979d45 </span></span><span class="line"><span class="cl">commit 0979d45f1b46f72730188c5c01b3f2c7f41b18e6 </span></span><span class="line"><span class="cl">Merge: 17e2629 c950910 </span></span><span class="line"><span class="cl">Author: Triple-Z &lt;me@triplez.cn&gt; </span></span><span class="line"><span class="cl">Date: Thu Mar <span class="m">31</span> 01:29:31 <span class="m">2022</span> +0800 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> R4 merge branch <span class="s1">&#39;green&#39;</span> into <span class="s1">&#39;red&#39;</span> </span></span></code></pre></td></tr></table> </div> </div><p>让我们再来试一次。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-10-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-10-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git cherry-pick -m <span class="m">1</span> 0979d45 </span></span></code></pre></td></tr></table> </div> </div><p><code>cherry-pick</code> 圆满完成!此时再看一下当前的提交记录,则发现在 <code>cp-single-merge-commit</code> 分支上产生了一个新的 <code>R4</code> 提交。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-11-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-11-1">1</a> </span><span class="lnt" id="hl-11-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-11-2">2</a> </span><span class="lnt" id="hl-11-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-11-3">3</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order </span></span><span class="line"><span class="cl">* 987aba7 <span class="o">(</span>HEAD -&gt; cp-single-merge-commit<span class="o">)</span> R4 merge branch <span class="s1">&#39;green&#39;</span> into <span class="s1">&#39;red&#39;</span> </span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit </span></span></code></pre></td></tr></table> </div> </div><p>现在我们再来讲讲刚刚的 <code>-m 1</code> 发生了什么。如果现在去看 <code>cp-single-merge-commit</code> 这个测试分支上的文件,则会发现有 <code>green</code> ,而没有 <code>red</code> 。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-12-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-1">1</a> </span><span class="lnt" id="hl-12-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-2">2</a> </span><span class="lnt" id="hl-12-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-3">3</a> </span><span class="lnt" id="hl-12-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-12-4">4</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ls -lh </span></span><span class="line"><span class="cl">total <span class="m">16</span> </span></span><span class="line"><span class="cl">-rw-r--r-- <span class="m">1</span> triplez staff 18B <span class="m">4</span> <span class="m">7</span> 19:06 green </span></span><span class="line"><span class="cl">-rw-r--r-- <span class="m">1</span> triplez staff 5B <span class="m">3</span> <span class="m">31</span> 01:29 init </span></span></code></pre></td></tr></table> </div> </div><p>这是因为我们在选取 merge commit 时,使用的是 mainline 1 ,即 <code>red</code> 分支。因此 <code>cherry-pick</code> 事实是以 <code>red</code> 为基础,寻找 mainline 2 <code>green</code> 分支与 <code>red</code> 的差异,选取的就是 <code>green</code> 分支上所做的修改了。</p> <h2 id="revision-range">Revision range</h2> <p>Git 中可用多种方法来表示 revision (版本,或修订快照)<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>,这里我们主要讨论 revision range(版本区间)<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>。</p> <p>对于 revision range,有以下六种表示法:</p> <ol> <li> <p><code>^&lt;rev&gt;</code> :(脱字符-表示法)表示<strong>排除</strong> <code>&lt;rev&gt;</code> 以及它<u>所有可到达</u>的父辈 commit。</p> </li> <li> <p><code>&lt;r1&gt;..&lt;r2&gt;</code>(两点-范围表示法):等同于 <code>^r1 r2</code> ,即 <strong>包含</strong> <code>&lt;r2&gt;</code> 以及其可到达的父辈 commit ,并<strong>排除</strong> <code>&lt;r1&gt;</code> 以及其可到达的父辈 commit。</p> <blockquote> <p>如果需要包括 <code>&lt;r1&gt;</code>,可使用这种写法:<code>&lt;r1&gt;^..&lt;r2&gt;</code> 。</p> </blockquote> </li> <li> <p><code>&lt;r1&gt;...&lt;r2&gt;</code> (三点-对称差分表示法):<strong>包含</strong>所有 <code>&lt;r1&gt;</code> <u>或</u> <code>&lt;r2&gt;</code> 及其可到达的父辈 commit,并<strong>排除</strong> <code>&lt;r1&gt;</code> <u>和</u> <code>&lt;r2&gt;</code> 两者可到达的<u>共同</u>父辈 commit。</p> </li> <li> <p><code>&lt;rev&gt;^@</code> :<strong>包含</strong> <code>&lt;rev&gt;</code> 的所有父辈,但<strong>排除</strong> <code>&lt;rev&gt;</code> 本身。</p> </li> <li> <p><code>&lt;rev&gt;^!</code> :<strong>包含</strong> <code>&lt;rev&gt;</code> 本身,但<strong>排除</strong> <code>&lt;rev&gt;</code> 所有父辈。即表示单个 <code>&lt;rev&gt;</code> commit。</p> <blockquote> <p>注意: <code>&lt;rev&gt;</code> (表示 <code>&lt;rev&gt;</code> 及其所有父辈)在 revision range 的语境中不同于 <code>&lt;rev&gt;^!</code> 。仅有指定 <code>--no-walk</code> 参数时,两者才可以认为是相同的(都仅表示 <code>&lt;rev&gt;</code> 本身)。</p> </blockquote> </li> <li> <p><code>&lt;rev&gt;^-[&lt;n&gt;]</code> :<strong>包含</strong> <code>&lt;rev&gt;</code> 及其所有父辈,但<strong>排除</strong> <code>&lt;rev&gt;</code> 的第 <code>&lt;n&gt;</code> 个 parent 及其可到达的所有父辈。 <code>&lt;n&gt;</code> 的缺省值为 1。</p> </li> </ol> <p>看起来很复杂,我们来用文中的场景来举两个范围表示法的例子。</p> <p>首先,考虑 <code>&lt;r1&gt;</code> 和 <code>&lt;r2&gt;</code> 都在同一分支上的情况,如 <code>G1 (05719c8)</code> 和 <code>G3 (c950910)</code>。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-13-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-1">1</a> </span><span class="lnt" id="hl-13-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-2">2</a> </span><span class="lnt" id="hl-13-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-3">3</a> </span><span class="lnt" id="hl-13-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-4">4</a> </span><span class="lnt" id="hl-13-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-13-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span> </span></span><span class="line"><span class="cl">$ git checkout master </span></span><span class="line"><span class="cl">$ git checkout -b cp-range-same-branch </span></span><span class="line"><span class="cl"><span class="c1"># G1 的提交 SHA 值为 05719c8,G3 的 SHA 值为 c950910</span> </span></span><span class="line"><span class="cl">$ git cherry-pick 05719c8^..c950910 </span></span></code></pre></td></tr></table> </div> </div><p><code>05719c8(G1)^..c950910(G3)</code> 的含义应当是:</p> <ul> <li><strong>包含</strong> <code>G3</code> 及其所有父辈。</li> <li>并<strong>排除</strong> <code>G1</code> 的所有父辈(不排除 <code>G1</code>)。</li> </ul> <p>因此结果应当是选出从 <code>G1</code> 到 <code>G3</code> 的所有提交,示意图如下,黄色为被包含的节点,灰色则代表被排除的节点。</p> <figure class="align-center "> <img loading="lazy" src="https://blog.triplez.cn/img/mastering-git-cherry-pick/git-cherry-pick-g1-g3.jpg#center" alt="git cheery-pick G1^..G3"/> <figcaption> git cheery-pick G1^..G3 </figcaption> </figure> <p>让我们再看看当前的提交记录。Bingo! <code>G1</code> ,<code>G2</code> ,<code>G3</code> 这三个提交已经被选取出来了。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-14-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-1">1</a> </span><span class="lnt" id="hl-14-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-2">2</a> </span><span class="lnt" id="hl-14-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-3">3</a> </span><span class="lnt" id="hl-14-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-4">4</a> </span><span class="lnt" id="hl-14-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-14-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order </span></span><span class="line"><span class="cl">* 32eac39 <span class="o">(</span>HEAD -&gt; cp-range-same-branch<span class="o">)</span> G3 </span></span><span class="line"><span class="cl">* d3b1130 G2 </span></span><span class="line"><span class="cl">* c82c4c7 G1 </span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit </span></span></code></pre></td></tr></table> </div> </div><p>那 <code>&lt;r1&gt;</code> 和 <code>&lt;r2&gt;</code> 在不同分支上,是什么情况呢?</p> <p>我们以 <code>G1 (05719c8)</code> 和 <code>B2 (69edfc9)</code> 作为用例。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-15-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-1">1</a> </span><span class="lnt" id="hl-15-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-2">2</a> </span><span class="lnt" id="hl-15-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-3">3</a> </span><span class="lnt" id="hl-15-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-4">4</a> </span><span class="lnt" id="hl-15-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-15-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 回到 master 新建一条分支用于测试</span> </span></span><span class="line"><span class="cl">$ git checkout master </span></span><span class="line"><span class="cl">$ git checkout -b cp-range-diff-branch </span></span><span class="line"><span class="cl"><span class="c1"># G1 的提交 SHA 值为 05719c8,B2 的 SHA 值为 69edfc9</span> </span></span><span class="line"><span class="cl">$ git cherry-pick 05719c8^..69edfc9 </span></span></code></pre></td></tr></table> </div> </div><p><code>05719c8(G1)^..69edfc9(B2)</code> 的含义应当是:</p> <ul> <li><strong>包含</strong> <code>B2</code> 及其所有父辈。</li> <li>并<strong>排除</strong> <code>G1</code> 的所有父辈(不排除 <code>G1</code>)。</li> </ul> <p>由于 <code>B2</code> 及其所有父辈中,并不包括 <code>G1</code>。因此我们可以将 <code>G1^..B2</code> 理解为包含 <code>B2</code> 及其所有父辈,且排除 <code>B2</code> 和 <code>G1</code> 的共同父辈后的结果。自然就只剩下 <code>B1</code> 和 <code>B2</code> 两个 commit 了。示意图如下,黄色为被包含的节点,灰色则代表被排除的节点。</p> <figure class="align-center "> <img loading="lazy" src="https://blog.triplez.cn/img/mastering-git-cherry-pick/git-cherry-pick-g1-b2.jpg#center" alt="git cherry-pick G1^..B2"/> <figcaption> git cherry-pick G1^..B2 </figcaption> </figure> <p>让我们再看看当前的提交记录,确实是只选择了 <code>B1</code> 和 <code>B2</code> 两个提交。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-16-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-1">1</a> </span><span class="lnt" id="hl-16-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-2">2</a> </span><span class="lnt" id="hl-16-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-3">3</a> </span><span class="lnt" id="hl-16-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-16-4">4</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git --no-pager log --oneline --graph --date-order </span></span><span class="line"><span class="cl">* e63f214 <span class="o">(</span>HEAD -&gt; cp-range-diff-branch<span class="o">)</span> B2 </span></span><span class="line"><span class="cl">* aed6717 B1 </span></span><span class="line"><span class="cl">* 2787f8f <span class="o">(</span>master<span class="o">)</span> init commit </span></span></code></pre></td></tr></table> </div> </div><h2 id="rerere">Rerere</h2> <p>Rerere 是“重用已记录的冲突解决方案(reuse recorded resolution)”,它是一种简化冲突解决的方法<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>。</p> <p>如果你经常进行大量的 merge, rebase 或 cherry-pick,或在维护一个长期不同于主干的分支<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>,那么非常建议开启 <code>rerere</code> 功能。</p> <p>开启 <code>rerere</code> 非常简单,仅需要进行一次全局配置即可。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-17-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-17-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ git config --global rerere.enabled <span class="nb">true</span> </span></span></code></pre></td></tr></table> </div> </div><blockquote> <p>在本地仓库中直接创建 <code>.git/rr-cache</code> 文件夹,也可以为该仓库开启 <code>rerere</code>。</p> </blockquote> <h2 id="whats-next">What&rsquo;s next</h2> <p>在笔者撰写该文的过程中,也看到了 Microsoft 的 Raymond Chen 写的 <a href="https://devblogs.microsoft.com/oldnewthing/20180323-01/?p=98325">Stop cherry-picking, start merging 系列文章</a>,他在其中提及了许多工程实践中 cherry-pick 可能导致的 pitfall。接下来的时间里,笔者将会逐一阅读该系列文章,并根据文中案例去分析 cherry-pick 是否能够在常用软件开发工作流给我们带来足够的收益,以及,是否应该 stop cherry-picking, start merging。</p> <p>只有在深入了解工具后,我们才能更好地运用工具,真正实现效率提升。</p> <div class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1"> <p>git-cherry-pick &lt;commit&gt;&hellip; <br/><a href="https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt-ltcommitgt82308203">https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt-ltcommitgt82308203</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2"> <p>git-rev-list &ndash;no-walk<br/><a href="https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---no-walksortedunsorted">https://git-scm.com/docs/git-rev-list#Documentation/git-rev-list.txt---no-walksortedunsorted</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3"> <p>git-cherry-pick -m, &ndash;mainline<br/><a href="https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt--mltparent-numbergt">https://git-scm.com/docs/git-cherry-pick#Documentation/git-cherry-pick.txt--mltparent-numbergt</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:4"> <p>gitrevisions: Specifying Revisions<br/><a href="https://git-scm.com/docs/gitrevisions/#_specifying_revisions">https://git-scm.com/docs/gitrevisions/#_specifying_revisions</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:5"> <p>gitrevisions: Specifying Ranges<br/><a href="https://git-scm.com/docs/gitrevisions/#_specifying_ranges">https://git-scm.com/docs/gitrevisions/#_specifying_ranges</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:6"> <p>Pro Git (zh): Git 工具 - Rerere<br/><a href="https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-Rerere#ef_rerere">https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-Rerere#ef_rerere</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:7"> <p>Pro Git (en): Git Tools - Rerere<br/><a href="https://git-scm.com/book/en/v2/Git-Tools-Rerere">https://git-scm.com/book/en/v2/Git-Tools-Rerere</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:8"> <p>Pro Git (zh): 分布式 Git - 维护项目 - Rerere<br/><a href="https://git-scm.com/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E7%BB%B4%E6%8A%A4%E9%A1%B9%E7%9B%AE#_rerere">https://git-scm.com/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E7%BB%B4%E6%8A%A4%E9%A1%B9%E7%9B%AE#_rerere</a>&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </div>

2022/4/7
阅读更多

通过 systemd 服务配置链接动态库

<h2 id="序">序</h2> <p>在设置动态链接的方法中,<code>rpath</code> 有其自身的问题<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> , <code>ld.so.conf</code> 为 OS 全局配置,可能会因为单一服务的动态库版本而影响到其他服务,不是一个优雅的方法。</p> <p>那如何才能不借助 <code>rpath</code> 和 <code>ld.so.conf</code> ,即能使目标服务找到对应的动态链接库,又能不影响其他服务呢?</p> <h2 id="ld_library_path">LD_LIBRARY_PATH</h2> <p>对于动态链接库的路径配置而言,除了 <code>rpath</code> 和 <code>ld.so.conf</code> ,还有 <code>LD_LIBRARY_PATH</code>。只要在二进制启动的环境中设置 <code>LD_LIBRARY_PATH</code> 变量,则 <code>glibc</code> 会将该变量中的路径配置作为动态链接库的查找路径之一,使得二进制可执行文件能够正常链接到其依赖的动态链接库。</p> <p><code>LD_LIBRARY_PATH</code> 会在当前 shell session 中“全局”生效,不过,由于笔者服务采用 systemd 来管理其生命周期,按照上述思路,应该只需要在服务启动前,将 <code>LD_LIBRARY_PATH</code> 注入启动环境即可,这样还能够借助 systemd 来实现一定程度的环境“隔离”。systemd 服务配置中的 <code>Environment</code> 字段是用于描述启动环境的环境变量的,我们先在这里加入 <code>LD_LIBRARY_PATH</code> (如下),重新加载 systemd 配置并重启服务,观察效果。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-12">12</a> </span><span class="lnt" id="hl-0-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-13">13</a> </span><span class="lnt" id="hl-0-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-14">14</a> </span><span class="lnt" id="hl-0-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-15">15</a> </span><span class="lnt" id="hl-0-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-16">16</a> </span><span class="lnt" id="hl-0-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-17">17</a> </span><span class="lnt" id="hl-0-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-18">18</a> </span><span class="lnt" id="hl-0-19"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-19">19</a> </span><span class="lnt" id="hl-0-20"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-20">20</a> </span><span class="lnt" id="hl-0-21"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-21">21</a> </span><span class="lnt" id="hl-0-22"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-22">22</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"> [Unit] </span></span><span class="line"><span class="cl"> Description=Nginx </span></span><span class="line"><span class="cl"> After=syslog.target network.target remote-fs.target nss-lookup.target </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> [Service] </span></span><span class="line"><span class="cl"> User=triplez </span></span><span class="line"><span class="cl"> Group=triplez </span></span><span class="line"><span class="cl"> Type=forking </span></span><span class="line"><span class="cl"><span class="gi">+Environment=&#34;LD_LIBRARY_PATH=/your/path/to/openssl/lib:/your/path/to/jemalloc/lib:/your/path/to/luajit/lib&#34; </span></span></span><span class="line"><span class="cl"><span class="gi"></span> PIDFile=/run/nginx/nginx.pid </span></span><span class="line"><span class="cl"> RuntimeDirectory=nginx </span></span><span class="line"><span class="cl"> RuntimeDirectoryMode=0755 </span></span><span class="line"><span class="cl"> ExecStartPre=/your/path/to/nginx -t </span></span><span class="line"><span class="cl"> ExecStart=/your/path/to/nginx </span></span><span class="line"><span class="cl"> ExecReload=/bin/kill -s HUP $MAINPID </span></span><span class="line"><span class="cl"> ExecStop=/bin/kill -s QUIT $MAINPID </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> PrivateTmp=true </span></span><span class="line"><span class="cl"> Restart=always </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> [Install] </span></span><span class="line"><span class="cl"> WantedBy=multi-user.target </span></span></code></pre></td></tr></table> </div> </div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1">1</a> </span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo systemctl daemon-reload </span></span><span class="line"><span class="cl">$ sudo systemctl restart nginx.service </span></span></code></pre></td></tr></table> </div> </div><p>事实上,对于大多数服务,只需要加入环境变量即可解决问题。但很不幸的是,由于我们的服务需要监听 1024 及以下端口(如 80、443),笔者对二进制可执行文件进行了 <code>setcap</code> 操作,赋予其监听低位端口的能力。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1">1</a> </span><span class="lnt" id="hl-2-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo getcap /your/path/to/nginx </span></span><span class="line"><span class="cl">/your/path/to/nginx <span class="o">=</span> cap_net_bind_service+ep </span></span></code></pre></td></tr></table> </div> </div><h2 id="capability">Capability</h2> <p>由于该文件含有 capability,<code>glibc</code> 会将 <code>LD_LIBRARY_PATH</code> 忽略<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>,导致服务还是无法正确链接到动态库上。</p> <p>了解原因之后,接下来要做的事情也很清晰了:一是先将二进制可执行文件上的 capability 移除(<code>chown</code> 就可以将其移除, <code>setcap -ep</code> 亦可),二是利用 systemd 的服务配置来实现配置 capability (如下)。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1"> 1</a> </span><span class="lnt" id="hl-3-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-2"> 2</a> </span><span class="lnt" id="hl-3-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-3"> 3</a> </span><span class="lnt" id="hl-3-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-4"> 4</a> </span><span class="lnt" id="hl-3-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-5"> 5</a> </span><span class="lnt" id="hl-3-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-6"> 6</a> </span><span class="lnt" id="hl-3-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-7"> 7</a> </span><span class="lnt" id="hl-3-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-8"> 8</a> </span><span class="lnt" id="hl-3-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-9"> 9</a> </span><span class="lnt" id="hl-3-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-10">10</a> </span><span class="lnt" id="hl-3-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-11">11</a> </span><span class="lnt" id="hl-3-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-12">12</a> </span><span class="lnt" id="hl-3-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-13">13</a> </span><span class="lnt" id="hl-3-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-14">14</a> </span><span class="lnt" id="hl-3-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-15">15</a> </span><span class="lnt" id="hl-3-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-16">16</a> </span><span class="lnt" id="hl-3-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-17">17</a> </span><span class="lnt" id="hl-3-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-18">18</a> </span><span class="lnt" id="hl-3-19"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-19">19</a> </span><span class="lnt" id="hl-3-20"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-20">20</a> </span><span class="lnt" id="hl-3-21"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-21">21</a> </span><span class="lnt" id="hl-3-22"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-22">22</a> </span><span class="lnt" id="hl-3-23"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-23">23</a> </span><span class="lnt" id="hl-3-24"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-24">24</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"> [Unit] </span></span><span class="line"><span class="cl"> Description=Nginx </span></span><span class="line"><span class="cl"> After=syslog.target network.target remote-fs.target nss-lookup.target </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> [Service] </span></span><span class="line"><span class="cl"> User=triplez </span></span><span class="line"><span class="cl"> Group=triplez </span></span><span class="line"><span class="cl"> Type=forking </span></span><span class="line"><span class="cl"> Environment=&#34;LD_LIBRARY_PATH=/your/path/to/openssl/lib:/your/path/to/jemalloc/lib:/your/path/to/luajit/lib&#34; </span></span><span class="line"><span class="cl"> PIDFile=/run/nginx/nginx.pid </span></span><span class="line"><span class="cl"> RuntimeDirectory=nginx </span></span><span class="line"><span class="cl"> RuntimeDirectoryMode=0755 </span></span><span class="line"><span class="cl"> ExecStartPre=/your/path/to/nginx -t </span></span><span class="line"><span class="cl"> ExecStart=/your/path/to/nginx </span></span><span class="line"><span class="cl"> ExecReload=/bin/kill -s HUP $MAINPID </span></span><span class="line"><span class="cl"> ExecStop=/bin/kill -s QUIT $MAINPID </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="gi">+CapabilityBoundingSet=CAP_NET_BIND_SERVICE </span></span></span><span class="line"><span class="cl"><span class="gi">+AmbientCapabilities=CAP_NET_BIND_SERVICE </span></span></span><span class="line"><span class="cl"><span class="gi"></span> PrivateTmp=true </span></span><span class="line"><span class="cl"> Restart=always </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> [Install] </span></span><span class="line"><span class="cl"> WantedBy=multi-user.target </span></span></code></pre></td></tr></table> </div> </div><p>再次重新加载 systemd 配置并重启服务,问题完美解决。</p> <div class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1"> <p>RpathIssue<br/><a href="https://wiki.debian.org/RpathIssue">https://wiki.debian.org/RpathIssue</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:2"> <p>Use of file capabilities disables LD_LIBRARY_PATH - Red Hat Bugzilla<br/><a href="https://bugzilla.redhat.com/show_bug.cgi?id=448594">https://bugzilla.redhat.com/show_bug.cgi?id=448594</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> <li id="fn:3"> <p>shared libraries - Linux capabilities (setcap) seems to disable LD_LIBRARY_PATH - Stack Overflow<br/><a href="https://stackoverflow.com/questions/9843178/linux-capabilities-setcap-seems-to-disable-ld-library-path">https://stackoverflow.com/questions/9843178/linux-capabilities-setcap-seems-to-disable-ld-library-path</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </div>

2022/3/31
阅读更多

chaosblade 生成指定 CPU 利用率负载的原理

<p>最近笔者在做的一个降级功能,与机器资源情况密切相关。然而在测试时发现控制 CPU 利用率来构造测试条件,并不是一个容易的事情。借助时间片的思想,笔者用一个非常简单的 shell 一定程度上解决了这个问题。但转念一想,对于混沌测试的软件,这应该是个必备能力。查找了一下 chaosblade 的相关资料,果然支持生成指定 CPU 利用率的负载。故读了读其这部分源码,看看它是怎么实现的。</p> <p>使用 <code>chaosblade</code> 来构造指定 CPU 利用率的负载非常简单:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1">1</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">blade create cpu load --cpu-percent <span class="m">80</span> </span></span></code></pre></td></tr></table> </div> </div><p>即能够生成使 CPU 利用率到达 80% 的负载。</p> <p><code>burncpu</code> 的核心逻辑位于这里: <a href="https://github.com/chaosblade-io/chaosblade-exec-os/blob/318c52d83a851bc75012abc7d880d4f440f1f972/exec/bin/burncpu/burncpu.go#L140-L168">https://github.com/chaosblade-io/chaosblade-exec-os/blob/318c52d83a851bc75012abc7d880d4f440f1f972/exec/bin/burncpu/burncpu.go#L140-L168</a></p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-13">13</a> </span><span class="lnt" id="hl-1-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-14">14</a> </span><span class="lnt" id="hl-1-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-15">15</a> </span><span class="lnt" id="hl-1-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-16">16</a> </span><span class="lnt" id="hl-1-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-17">17</a> </span><span class="lnt" id="hl-1-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-18">18</a> </span><span class="lnt" id="hl-1-19"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-19">19</a> </span><span class="lnt" id="hl-1-20"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-20">20</a> </span><span class="lnt" id="hl-1-21"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-21">21</a> </span><span class="lnt" id="hl-1-22"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-22">22</a> </span><span class="lnt" id="hl-1-23"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-23">23</a> </span><span class="lnt" id="hl-1-24"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-24">24</a> </span><span class="lnt" id="hl-1-25"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-25">25</a> </span><span class="lnt" id="hl-1-26"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-26">26</a> </span><span class="lnt" id="hl-1-27"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-27">27</a> </span><span class="lnt" id="hl-1-28"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-28">28</a> </span><span class="lnt" id="hl-1-29"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-29">29</a> </span><span class="lnt" id="hl-1-30"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-30">30</a> </span><span class="lnt" id="hl-1-31"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-31">31</a> </span><span class="lnt" id="hl-1-32"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-32">32</a> </span><span class="lnt" id="hl-1-33"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-33">33</a> </span><span class="lnt" id="hl-1-34"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-34">34</a> </span><span class="lnt" id="hl-1-35"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-35">35</a> </span><span class="lnt" id="hl-1-36"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-36">36</a> </span><span class="lnt" id="hl-1-37"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-37">37</a> </span><span class="lnt" id="hl-1-38"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-38">38</a> </span><span class="lnt" id="hl-1-39"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-39">39</a> </span><span class="lnt" id="hl-1-40"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-40">40</a> </span><span class="lnt" id="hl-1-41"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-41">41</a> </span><span class="lnt" id="hl-1-42"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-42">42</a> </span><span class="lnt" id="hl-1-43"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-43">43</a> </span><span class="lnt" id="hl-1-44"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-44">44</a> </span><span class="lnt" id="hl-1-45"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-45">45</a> </span><span class="lnt" id="hl-1-46"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-46">46</a> </span><span class="lnt" id="hl-1-47"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-47">47</a> </span><span class="lnt" id="hl-1-48"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-48">48</a> </span><span class="lnt" id="hl-1-49"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-49">49</a> </span><span class="lnt" id="hl-1-50"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-50">50</a> </span><span class="lnt" id="hl-1-51"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-51">51</a> </span><span class="lnt" id="hl-1-52"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-52">52</a> </span><span class="lnt" id="hl-1-53"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-53">53</a> </span><span class="lnt" id="hl-1-54"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-54">54</a> </span><span class="lnt" id="hl-1-55"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-55">55</a> </span><span class="lnt" id="hl-1-56"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-56">56</a> </span><span class="lnt" id="hl-1-57"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-57">57</a> </span><span class="lnt" id="hl-1-58"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-58">58</a> </span><span class="lnt" id="hl-1-59"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-59">59</a> </span><span class="lnt" id="hl-1-60"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-60">60</a> </span><span class="lnt" id="hl-1-61"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-61">61</a> </span><span class="lnt" id="hl-1-62"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-62">62</a> </span><span class="lnt" id="hl-1-63"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-63">63</a> </span><span class="lnt" id="hl-1-64"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-64">64</a> </span><span class="lnt" id="hl-1-65"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-65">65</a> </span><span class="lnt" id="hl-1-66"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-66">66</a> </span><span class="lnt" id="hl-1-67"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-67">67</a> </span><span class="lnt" id="hl-1-68"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-68">68</a> </span><span class="lnt" id="hl-1-69"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-69">69</a> </span><span class="lnt" id="hl-1-70"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-70">70</a> </span><span class="lnt" id="hl-1-71"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-71">71</a> </span><span class="lnt" id="hl-1-72"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-72">72</a> </span><span class="lnt" id="hl-1-73"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-73">73</a> </span><span class="lnt" id="hl-1-74"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-74">74</a> </span><span class="lnt" id="hl-1-75"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-75">75</a> </span><span class="lnt" id="hl-1-76"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-76">76</a> </span><span class="lnt" id="hl-1-77"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-77">77</a> </span><span class="lnt" id="hl-1-78"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-78">78</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">burnCpu</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">runtime</span><span class="p">.</span><span class="nf">GOMAXPROCS</span><span class="p">(</span><span class="nx">cpuCount</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">totalCpuPercent</span> <span class="p">[]</span><span class="kt">float64</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">curProcess</span> <span class="o">*</span><span class="nx">process</span><span class="p">.</span><span class="nx">Process</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">curCpuPercent</span> <span class="kt">float64</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">totalCpuPercent</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">cpu</span><span class="p">.</span><span class="nf">Percent</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span> <span class="c1">// 获取当前所有 CPU 一秒内平均利用率 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">curProcess</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">process</span><span class="p">.</span><span class="nf">NewProcess</span><span class="p">(</span><span class="nb">int32</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nf">Getpid</span><span class="p">()))</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">curCpuPercent</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">curProcess</span><span class="p">.</span><span class="nf">CPUPercent</span><span class="p">()</span> <span class="c1">// 获取当前进程的 CPU 利用率 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">otherCpuPercent</span> <span class="o">:=</span> <span class="p">(</span><span class="mf">100.0</span> <span class="o">-</span> <span class="p">(</span><span class="nx">totalCpuPercent</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="nx">curCpuPercent</span><span class="p">))</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="c1">// 除去已有进程,可操作的 CPU 利用率。值的范围为 [0, 1] </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">3</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// timer 3s </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">case</span> <span class="o">&lt;-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="nx">totalCpuPercent</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">cpu</span><span class="p">.</span><span class="nf">Percent</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">curCpuPercent</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">curProcess</span><span class="p">.</span><span class="nf">CPUPercent</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">otherCpuPercent</span> <span class="p">=</span> <span class="p">(</span><span class="mf">100.0</span> <span class="o">-</span> <span class="p">(</span><span class="nx">totalCpuPercent</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="nx">curCpuPercent</span><span class="p">))</span> <span class="o">/</span> <span class="mf">100.0</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}()</span> <span class="c1">// 每 3s 更新一次 totalCpuPercent, curCpuPercent 和 otherCpuPercent </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">climbTime</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> <span class="c1">// 不需要爬坡时间 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">slopePercent</span> <span class="p">=</span> <span class="nb">float64</span><span class="p">(</span><span class="nx">cpuPercent</span><span class="p">)</span> <span class="c1">// 爬坡值与目标值一致 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// cpuCount 是由 runtime.NumCPU() 得来,获取的是当前 CPU 的逻辑核数量 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">cpuCount</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">busy</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">idle</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">all</span> <span class="o">:=</span> <span class="nb">int64</span><span class="p">(</span><span class="mi">10000000</span><span class="p">)</span> <span class="c1">// 设定 10ms 为一个周期 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">dx</span> <span class="o">:=</span> <span class="mf">0.0</span> </span></span><span class="line"><span class="cl"> <span class="nx">ds</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;</span> <span class="nx">i</span> <span class="p">=</span> <span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="mi">1000</span> <span class="p">{</span> <span class="c1">// 死循环 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">startTime</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">i</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span> <span class="c1">// 每 1000 次进入 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 这个赋值语句是整个 burncpu 的灵魂。 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 我们最终希望获得的是 slopePercent% 的 CPU 利用率 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 应该生成的 CPU 压力即为 (slopePercent - totalCpuPercent[0])% </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 理想条件下,我们只需对 (slopePercent - totalCpuPercent[0]) 个 0.1ms 时间片设置为 busy 状态即可 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 但由于系统中存在其他进程,burncpu 无法真正获得到 (slopePercent - totalCpuPercent[0]) 个 0.1ms 时间片 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 因此需要按比例放大时间片的个数,而这个比例则是当时 burncpu 实际可用的 CPU 利用率 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 将这个赋值语句转换为如下方程,则更好理解了: </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// slopePercent = totalCpuPercent + dx * otherCpuPercent </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ^ ^ ^ ^ </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 最终获得的CPU利用率 当前CPU利用率 一个周期内busy的时间片个数 burnCpu真正可操作的CPU比例 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">dx</span> <span class="p">=</span> <span class="p">(</span><span class="nx">slopePercent</span> <span class="o">-</span> <span class="nx">totalCpuPercent</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">/</span> <span class="nx">otherCpuPercent</span> </span></span><span class="line"><span class="cl"> <span class="nx">busy</span> <span class="p">=</span> <span class="nx">busy</span> <span class="o">+</span> <span class="nb">int64</span><span class="p">(</span><span class="nx">dx</span><span class="o">*</span><span class="mi">100000</span><span class="p">)</span> <span class="c1">// 有 dx 个 0.1ms 需要为 busy 状态 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">busy</span> <span class="p">&lt;</span> <span class="mi">0</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">busy</span> <span class="p">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">idle</span> <span class="p">=</span> <span class="nx">all</span> <span class="o">-</span> <span class="nx">busy</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">idle</span> <span class="p">&lt;</span> <span class="mi">0</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">idle</span> <span class="p">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nx">ds</span><span class="p">,</span> <span class="nx">_</span> <span class="p">=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">ParseDuration</span><span class="p">(</span><span class="nx">strconv</span><span class="p">.</span><span class="nf">FormatInt</span><span class="p">(</span><span class="nx">idle</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="o">+</span> <span class="s">&#34;ns&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">UnixNano</span><span class="p">()</span><span class="o">-</span><span class="nx">startTime</span> <span class="p">&lt;</span> <span class="nx">busy</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="c1">// 阻塞 CPU,使 CPU 位于 busy 状态,直至设定的时间片结束 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">time</span><span class="p">.</span><span class="nf">Sleep</span><span class="p">(</span><span class="nx">ds</span><span class="p">)</span> <span class="c1">// 空闲 CPU,使 CPU 处于 idle 状态,直至设定的时间片结束 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">Gosched</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}()</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{}</span> <span class="c1">// 阻塞 burnCpu 函数,保活 goroutines </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div>

2022/1/26
阅读更多

Lexer & Parser Toolchain

<blockquote> <p>推荐一个非常好的编译器工具链入门教程: <a href="https://pandolia.net/tinyc/index.html">https://pandolia.net/tinyc/index.html</a></p> </blockquote> <h2 id="lexer---flex">Lexer - flex</h2> <p>flex 文件格式:</p> <blockquote> <p><a href="https://pandolia.net/tinyc/ch8_flex.html">https://pandolia.net/tinyc/ch8_flex.html</a></p> </blockquote> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-11">11</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">%{ </span></span><span class="line"><span class="cl">Declarations </span></span><span class="line"><span class="cl">%} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">Definitions </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl">Rules </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">User subroutines </span></span></code></pre></td></tr></table> </div> </div><ul> <li> <p>Declarations:声明,会被原样复制入 <code>lex.yy.c</code> 。一般用于声明全局变量和函数。</p> </li> <li> <p>Definitions:定义,可以定义正则表达式的名字,用于 Rules 中使用,通过名字直接使用预定义的正则表达式。</p> </li> <li> <p>Rules:规则,每一行都是一条规则,由匹配模式 pattern (正则表达式)和事件 action (C 代码)组成。</p> </li> <li> <p>User subroutines:用户定义过程,会被原样复制到 <code>lex.yy.c</code> 的最末尾。</p> </li> <li> <p><code>yywrap()</code> 用于把多个输入文件打包成一个输入,当 <code>yylex</code> 将一个文件读入到结尾 EOF 时,会向 <code>yywrap</code> 询问是否继续。若需连续解析多个文件,需要在 yywrap 中打开文件,并返回 0。返回 1 则表示后面没有文件可以读取了,使得 <code>yylex</code> 函数结束。</p> </li> <li> <p><code>yytext</code>:刚刚匹配到的字符串的值。</p> </li> <li> <p><code>yyleng</code>:刚刚匹配到的字符串的长度。</p> </li> </ul> <p>如一个简单的计算器实现:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-13">13</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">%{ </span></span><span class="line"><span class="cl">#include &#34;y.tab.h&#34; </span></span><span class="line"><span class="cl">%} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl">[0-9]+ { yylval = atoi(yytext); return T_NUM; } </span></span><span class="line"><span class="cl">[-/+*()\n] { return yytext[0]; } </span></span><span class="line"><span class="cl">. { return 0; /* end when meet everything else */ } </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">int yywrap(void) { </span></span><span class="line"><span class="cl"> return 1; </span></span><span class="line"><span class="cl">} </span></span></code></pre></td></tr></table> </div> </div><h2 id="parser---yaccbison">Parser - Yacc/Bison</h2> <p>bison 可以认为是 yacc 的开源实现。</p> <p>Bison 文件的格式:</p> <blockquote> <p><a href="https://pandolia.net/tinyc/ch13_bison.html">https://pandolia.net/tinyc/ch13_bison.html</a></p> </blockquote> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-10">10</a> </span><span class="lnt" id="hl-2-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-11">11</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">%{ </span></span><span class="line"><span class="cl">Declarations </span></span><span class="line"><span class="cl">%} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">Definitions </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl">Productions </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">User subroutines </span></span></code></pre></td></tr></table> </div> </div><ul> <li> <p>Declarations:声明,会被原样复制入 <code>y.tab.c</code> 。一般用于声明全局变量和函数。</p> </li> <li> <p>Definitions:定义,可以定义 bison 专有的变量。</p> <ul> <li><code>%token</code>:单字符 token (token type 值与字符的 ASCII 码相同)不需要使用 <code>%token</code> 进行预定义,其他类型的 token 都需要使用 <code>%token</code> 进行预定义。bison 会自动为 token 分配一个编号,并写入 <code>y.tab.h</code> 中,因此在 flex 文件中是可以直接调用的。</li> <li><code>%left</code> 、<code>%right</code>:表示符号是左(左向右)、右(右向左)结合的。</li> <li><code>%nonassoc</code> :表示符号是不可结合的,如 <code>x op y op z</code> 是非法的。</li> <li><code>%prec</code>:上下文依赖的优先级,如「负号」就是一个很典型的例子,见 <a href="https://www.gnu.org/software/bison/manual/html_node/Contextual-Precedence.html">Context-Dependent Precedence</a>。</li> <li>更多定义可见 <a href="https://www.gnu.org/software/bison/manual/html_node/Declarations.html">bison Declarations</a>。</li> </ul> </li> <li> <p>Productions:</p> <ul> <li> <p><code>:</code> 代表 <code>-&gt;</code>,或 EBNF 式中的 <code>=</code>。</p> </li> <li> <p><code>|</code> 用于分隔同一个非终结符的不同产生式。</p> </li> <li> <p><code>/* empty */</code> ,若产生式右边为 $\epsilon$ 时,不需要写任何符号,可写为注释 <code>/* empty */</code>。</p> </li> <li> <p><code>;</code> 表示结束一个非终结符的产生式。</p> </li> <li> <p>每个产生式后面花括号内,都是一段 C 代码,可在产生式被应用时执行。</p> </li> <li> <p>例:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1">1</a> </span><span class="lnt" id="hl-3-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-2">2</a> </span><span class="lnt" id="hl-3-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-3">3</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">s : S E &#39;\n&#39; { printf(&#34;ans = %d\n&#34;, $2); } </span></span><span class="line"><span class="cl"> | /* empty */ { /* empty */} </span></span><span class="line"><span class="cl"> ; </span></span></code></pre></td></tr></table> </div> </div></li> </ul> </li> <li> <p>User subroutines:用户定义过程,会被原样复制到 <code>y.tab.c</code> 的最末尾。</p> </li> </ul> <p>bison 会将语法产生式以及符号优先级转换成一个 C 语言的 LALR(1) 动作表,输出到 <code>y.tab.c</code> 中。并会将这个动作表转换为可读形式输出至 <code>y.output</code> 中。</p> <p>bison 会根据自定义语法文件在 <code>y.tab.c</code> 中生成一个函数 <code>int yyparse(void)</code> 。这个函数按照 LR(1) 解析流程,对词法分析中得到的 token 流进行解析。每当读取下一个符号时,就会执行一次 <code>x = yylex()</code> 。每当要执行一个折叠动作(reduce)时,相应的产生式后的 C 代码将被执行,执行完后将相应的状态出栈。</p> <p>若 token 流不合法,<code>yyparse</code> 会在第一次出错的地方终止,并调用 <code>yyerror</code> 函数,最后返回 1。</p> <p>在 reduce 动作时,可用 <code>$1</code>, <code>$2</code> &hellip; <code>$n</code> 来引用属性栈的属性(可以认为是产生式中的第 n 个属性内容,并在最后将这个状态下的属性出栈。其中,<code>$$</code> 代表产生式左侧的终结符,可在 reduce 动作设置 <code>$$</code> 的值,最后将 <code>$$</code> 入栈。</p> <p>如一个简单的计算器实现:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-1"> 1</a> </span><span class="lnt" id="hl-4-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-2"> 2</a> </span><span class="lnt" id="hl-4-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-3"> 3</a> </span><span class="lnt" id="hl-4-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-4"> 4</a> </span><span class="lnt" id="hl-4-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-5"> 5</a> </span><span class="lnt" id="hl-4-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-6"> 6</a> </span><span class="lnt" id="hl-4-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-7"> 7</a> </span><span class="lnt" id="hl-4-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-8"> 8</a> </span><span class="lnt" id="hl-4-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-9"> 9</a> </span><span class="lnt" id="hl-4-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-10">10</a> </span><span class="lnt" id="hl-4-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-11">11</a> </span><span class="lnt" id="hl-4-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-12">12</a> </span><span class="lnt" id="hl-4-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-13">13</a> </span><span class="lnt" id="hl-4-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-14">14</a> </span><span class="lnt" id="hl-4-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-15">15</a> </span><span class="lnt" id="hl-4-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-16">16</a> </span><span class="lnt" id="hl-4-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-17">17</a> </span><span class="lnt" id="hl-4-18"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-18">18</a> </span><span class="lnt" id="hl-4-19"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-19">19</a> </span><span class="lnt" id="hl-4-20"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-20">20</a> </span><span class="lnt" id="hl-4-21"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-21">21</a> </span><span class="lnt" id="hl-4-22"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-22">22</a> </span><span class="lnt" id="hl-4-23"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-23">23</a> </span><span class="lnt" id="hl-4-24"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-24">24</a> </span><span class="lnt" id="hl-4-25"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-25">25</a> </span><span class="lnt" id="hl-4-26"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-26">26</a> </span><span class="lnt" id="hl-4-27"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-27">27</a> </span><span class="lnt" id="hl-4-28"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-28">28</a> </span><span class="lnt" id="hl-4-29"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-29">29</a> </span><span class="lnt" id="hl-4-30"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-30">30</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">%{ </span></span><span class="line"><span class="cl">#include &lt;stdio.h&gt; </span></span><span class="line"><span class="cl">void yyerror(const char* msg) {} </span></span><span class="line"><span class="cl">int yylex(); </span></span><span class="line"><span class="cl">%} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%token T_NUM </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%left &#39;+&#39; &#39;-&#39; </span></span><span class="line"><span class="cl">%left &#39;*&#39; &#39;/&#39; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">S : S E &#39;\n&#39; { printf(&#34;ans = %d\n&#34;, $2); } </span></span><span class="line"><span class="cl"> | /* empty */ { /* empty */ } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">E : E &#39;+&#39; E { $$ = $1 + $3; } </span></span><span class="line"><span class="cl"> | E &#39;-&#39; E { $$ = $1 - $3; } </span></span><span class="line"><span class="cl"> | E &#39;*&#39; E { $$ = $1 * $3; } </span></span><span class="line"><span class="cl"> | E &#39;/&#39; E { $$ = $1 / $3; } </span></span><span class="line"><span class="cl"> | T_NUM { $$ = $1; } </span></span><span class="line"><span class="cl"> | &#39;(&#39; E &#39;)&#39; { $$ = $2; } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">int main() { </span></span><span class="line"><span class="cl"> return yyparse(); </span></span><span class="line"><span class="cl">} </span></span></code></pre></td></tr></table> </div> </div>

2022/1/25
阅读更多

grub rescue>, Oops!

<p>教你如何修复丢失的 Linux GRUB 启动引导。</p> <h2 id="起因">起因</h2> <p>为了再将神船 Z7 用起来,笔者决定扩展其仅有 200GB 的游戏分区空间。然而由于可用空间的位置并不连续,Windows 自带磁盘工具无法处理这种情况,且笔者并不希望将分区转为动态分区,因此使用了一个第三方软件 <a href="https://www.easeus.com/partition-manager/epm-free.html">EaseUS Partition Master Free</a> 来完成磁盘数据的迁移和分区重分配工作。就在设置好分区,重启了之后,屏幕上赫然显示着一个命令行界面:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1">1</a> </span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2">2</a> </span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3">3</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">error: unknown filesystem. </span></span><span class="line"><span class="cl">Entering rescue mode... </span></span><span class="line"><span class="cl">grub rescue&gt; </span></span></code></pre></td></tr></table> </div> </div><p>Oops!难道重新分个区就把系统搞挂了吗?!<code>grub rescue&gt;</code> 这个命令行界面在我心中一直是噩梦般的存在,之前遇到都只能选择计算机三大法宝之一:重装来解决问题。</p> <p>既然双系统里的 Linux 进不去了,那我试试直接进 Windows?于是重启了一下机器,通过 BIOS 直接进入了系统盘位于 SSD 中的 Windows。好家伙,这能正常启动,那问题就不大了。</p> <p>Windows 磁盘分区的重分配工作得到了验证,看上去非常完美。笔者用于存储游戏的专用分区已经扩容至 ~450GB ,那接下来还是修复下 Linux 启动引导的问题吧。</p> <p>经过一番简单的搜索,笔者摸清了其中大概的原因:在进行 Windows 磁盘操作时,对整个 HDD 做了分区、合并等处理,导致 GPT 元数据发生了改变,Linux 的引导程序 GRUB 自然就无法找到之前的 <code>/boot</code> 启动分区了,这也是报出 <code>error: unknown filesystem</code> 的原因。</p> <h2 id="在-grub-rescue-中修复启动项">在 grub rescue 中修复启动项</h2> <p>由于是硬盘元数据发生了改变导致 GRUB 失效,那我们的思路就是更新 GRUB 中的配置。</p> <p>首先要找到目前 Linux 的 <code>/boot</code> 启动分区的位置,在 <code>grub rescue</code> 中,可以用 <code>ls</code> 来寻找:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1">1</a> </span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">grub rescue&gt; ls </span></span><span class="line"><span class="cl"><span class="o">(</span>hd0<span class="o">)</span> <span class="o">(</span>hd0,gpt7<span class="o">)</span> <span class="o">(</span>hd0,gpt6<span class="o">)</span> <span class="o">(</span>hd0,gpt5<span class="o">)</span> <span class="o">(</span>hd0,gpt4<span class="o">)</span> <span class="o">(</span>hd0,gpt3<span class="o">)</span> <span class="o">(</span>hd0,gpt2<span class="o">)</span> <span class="o">(</span>hd0,gpt1<span class="o">)</span> <span class="o">(</span>hd1<span class="o">)</span> <span class="o">(</span>hd1,gpt3<span class="o">)</span> <span class="o">(</span>hd1,gpt2<span class="o">)</span> <span class="o">(</span>hd1,gpt1<span class="o">)</span> </span></span></code></pre></td></tr></table> </div> </div><p>直接输入 <code>ls</code> ,即可得到所有的分区位置。但是启动分区到底是哪个呢?我们可以继续用 <code>ls</code> 来查找,先试试 <code>(hd0,gpt1)</code> 这个分区吧。</p> <blockquote> <p>此处要注意的是,我们要查找的是 <code>/boot/grub</code> 这个文件夹目录存在的分区。由于笔者的 <code>/boot</code> 启动分区是单独挂载的,所以查找的路径是 <code>&lt;partition&gt;/grub</code> 。若安装的 Linux 并没有单独挂载 <code>/boot</code> 启动分区,则查找路径应为 <code>&lt;partition&gt;/boot/grub</code>。</p> </blockquote> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1">1</a> </span><span class="lnt" id="hl-2-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">grub rescue&gt; ls <span class="o">(</span>hd0,gpt1<span class="o">)</span>/grub </span></span><span class="line"><span class="cl">error: unknown filesystem. </span></span></code></pre></td></tr></table> </div> </div><p>输出为 <code>error: unknown filesystem.</code> ,说明这个分区并不是我们想要的启动分区,那再试试下一个分区。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1">1</a> </span><span class="lnt" id="hl-3-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">grub rescue&gt; ls <span class="o">(</span>hd0,gpt2<span class="o">)</span>/grub </span></span><span class="line"><span class="cl">./ ../ x86_64-efi/ grubenv themes/ fonts/ grub.cfg </span></span></code></pre></td></tr></table> </div> </div><p>Bingo!这就是我们要找的东西丫!好,记下这个分区 <code>(hd0,gpt2)</code>,它就是我们宝贵的启动分区。</p> <p>接下来就是给 GRUB 修改启动配置,只要改两个参数即可:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-1">1</a> </span><span class="lnt" id="hl-4-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">grub rescue&gt; <span class="nb">set</span> <span class="nv">root</span><span class="o">=(</span>hd0,gpt2<span class="o">)</span> </span></span><span class="line"><span class="cl">grub rescue&gt; <span class="nb">set</span> <span class="nv">prefix</span><span class="o">=(</span>hd0,gpt2<span class="o">)</span>/grub </span></span></code></pre></td></tr></table> </div> </div><ul> <li>将 <code>root</code> 设置为启动分区。</li> <li>将 <code>prefix</code> 设置为 <code>grub</code> 安装文件夹。若 <code>/boot</code> 未挂载单独分区,也可能为 <code>&lt;partition&gt;/boot/grub</code>。</li> </ul> <p>配置也修改好了之后,重新使 GRUB 进入普通模式就可以找回我们的启动项了!</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-1">1</a> </span><span class="lnt" id="hl-5-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">grub rescue&gt; insmod normal </span></span><span class="line"><span class="cl">grub rescue&gt; normal </span></span></code></pre></td></tr></table> </div> </div><p>执行完后,应该能看到我们熟悉的 GRUB 引导界面了。</p> <h2 id="持久化">持久化</h2> <p>事实上,这样的修复只是暂时的,我们还需要对 GRUB 配置进行持久化的更新,否则每次开机都要这样操作一遍。</p> <p>进入 Linux,打开终端,输入如下命令,GRUB 就会自动更新配置。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-1">1</a> </span><span class="lnt" id="hl-6-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ sudo update-grub </span></span><span class="line"><span class="cl">$ sudo grub-install /dev/sda </span></span></code></pre></td></tr></table> </div> </div><p><code>/dev/sda</code> 为 Linux 的安装磁盘。</p> <p>可以再重启一次计算机,验证 Linux 启动引导是否修复成功。</p> <h2 id="references">References</h2> <ul> <li>Repair Linux boot failures in GRUB 2 rescue mode</br> <a href="https://www.howtoforge.com/tutorial/repair-linux-boot-with-grub-rescue/">https://www.howtoforge.com/tutorial/repair-linux-boot-with-grub-rescue/</a></li> <li>grub rescue 救援模式的处理</br> <a href="http://xstarcd.github.io/wiki/Linux/grub_rescue.html">http://xstarcd.github.io/wiki/Linux/grub_rescue.html</a></li> <li>Linux 系统引导失败,出现 grub rescue 恢复界面 - 少数派</br> <a href="https://sspai.com/post/55875">https://sspai.com/post/55875</a></li> </ul>

2021/11/28
阅读更多

浅谈 beancount 借款还款交易记录方法

<p>本文基于的假设是:友人 A 需要购买产品 B,但需要你来代他购买。探讨以下几种事件发生顺序的记账方法。</p> <p>除了自己已有的 <code>Assets:Bank:Z</code> 外,需要额外建立这些账户:</p> <ul> <li><code>Assets:Receivables:A</code></li> </ul> <h2 id="先转账再交易">先转账,再交易</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-12">12</a> </span><span class="lnt" id="hl-0-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-13">13</a> </span><span class="lnt" id="hl-0-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-14">14</a> </span><span class="lnt" id="hl-0-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-15">15</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">; 转账 </span></span><span class="line"><span class="cl">0000-00-00 * &#34;A transfer money&#34; </span></span><span class="line"><span class="cl"> Assets:Receivables:A -1000.00 CNY </span></span><span class="line"><span class="cl"> Assets:Bank:Z 1000.00 CNY </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; 此时 Assets:Bank:Z 账户起着代持资金的作用。 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; 交易 </span></span><span class="line"><span class="cl">0000-00-01 * &#34;Buy B for A&#34; </span></span><span class="line"><span class="cl"> Assets:Bank:Z -1000.00 CNY </span></span><span class="line"><span class="cl"> Assets:Receivables:A 1000.00 CNY </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; 整个过程结束后各账户余额: </span></span><span class="line"><span class="cl">; Assets:Bank:Z 0.00 CNY </span></span><span class="line"><span class="cl">; Assets:Receivables:A 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div><h2 id="先交易再转账垫付">先交易,再转账(垫付)</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1"> 1</a> </span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2"> 2</a> </span><span class="lnt" id="hl-1-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-3"> 3</a> </span><span class="lnt" id="hl-1-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-4"> 4</a> </span><span class="lnt" id="hl-1-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-5"> 5</a> </span><span class="lnt" id="hl-1-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-6"> 6</a> </span><span class="lnt" id="hl-1-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-7"> 7</a> </span><span class="lnt" id="hl-1-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-8"> 8</a> </span><span class="lnt" id="hl-1-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-9"> 9</a> </span><span class="lnt" id="hl-1-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-10">10</a> </span><span class="lnt" id="hl-1-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-11">11</a> </span><span class="lnt" id="hl-1-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-12">12</a> </span><span class="lnt" id="hl-1-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-13">13</a> </span><span class="lnt" id="hl-1-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-14">14</a> </span><span class="lnt" id="hl-1-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-15">15</a> </span><span class="lnt" id="hl-1-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-16">16</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">; 交易(垫付) </span></span><span class="line"><span class="cl">0000-00-00 * &#34;Buy B for A&#34; </span></span><span class="line"><span class="cl"> Assets:Receivables:A 1000.00 CNY </span></span><span class="line"><span class="cl"> Assets:Bank:Z -1000.00 CNY </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; 此时 Assets:Bank:Z 账户起着垫付资金的作用, </span></span><span class="line"><span class="cl">; Assets:Receivables:A 用于应收来自 A 的款项。 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; 转账(还款) </span></span><span class="line"><span class="cl">0000-00-01 * &#34;A transfer money&#34; </span></span><span class="line"><span class="cl"> Assets:Bank:Z 1000.00 CNY </span></span><span class="line"><span class="cl"> Assets:Receivables:A -1000.00 CNY </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; 交易结束后各账户余额: </span></span><span class="line"><span class="cl">; Assets:Bank:Z 0.00 CNY </span></span><span class="line"><span class="cl">; Assets:Receivables:A 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div><h2 id="转账和交易同步">转账和交易同步</h2> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1"> 1</a> </span><span class="lnt" id="hl-2-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-2"> 2</a> </span><span class="lnt" id="hl-2-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-3"> 3</a> </span><span class="lnt" id="hl-2-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-4"> 4</a> </span><span class="lnt" id="hl-2-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-5"> 5</a> </span><span class="lnt" id="hl-2-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-6"> 6</a> </span><span class="lnt" id="hl-2-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-7"> 7</a> </span><span class="lnt" id="hl-2-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-8"> 8</a> </span><span class="lnt" id="hl-2-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-9"> 9</a> </span><span class="lnt" id="hl-2-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-10">10</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">; 转账+交易 </span></span><span class="line"><span class="cl">0000-00-00 * &#34;A transfer money to buy B&#34; </span></span><span class="line"><span class="cl"> Assets:Receivables:A -1000.00 CNY </span></span><span class="line"><span class="cl"> Assets:Bank:Z 1000.00 CNY </span></span><span class="line"><span class="cl"> Assets:Bank:Z -1000.00 CNY </span></span><span class="line"><span class="cl"> Assets:Receivables:A 1000.00 CNY </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; 交易结束后各账户余额: </span></span><span class="line"><span class="cl">; Assets:Bank:Z 0.00 CNY </span></span><span class="line"><span class="cl">; Assets:Receivables:A 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div>

2021/8/10
阅读更多

月度记账工作流

<h2 id="准备工作">准备工作</h2> <ul> <li>一部装有 <code>beancount</code> 、<code>fava</code> 以及 <code>double-entry-generator</code> 的 Mac 或 PC。</li> <li>一部 iPhone 或 Android 手机。</li> <li>本地存储个人 <code>my-bookkeepings</code> 账本最新内容。</li> </ul> <h2 id="开始工作">开始工作!</h2> <details> <summary>Balance CheatSheet</summary> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-0-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-1"> 1</a> </span><span class="lnt" id="hl-0-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-2"> 2</a> </span><span class="lnt" id="hl-0-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-3"> 3</a> </span><span class="lnt" id="hl-0-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-4"> 4</a> </span><span class="lnt" id="hl-0-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-5"> 5</a> </span><span class="lnt" id="hl-0-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-6"> 6</a> </span><span class="lnt" id="hl-0-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-7"> 7</a> </span><span class="lnt" id="hl-0-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-8"> 8</a> </span><span class="lnt" id="hl-0-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-9"> 9</a> </span><span class="lnt" id="hl-0-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-10">10</a> </span><span class="lnt" id="hl-0-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-11">11</a> </span><span class="lnt" id="hl-0-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-12">12</a> </span><span class="lnt" id="hl-0-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-13">13</a> </span><span class="lnt" id="hl-0-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-14">14</a> </span><span class="lnt" id="hl-0-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-15">15</a> </span><span class="lnt" id="hl-0-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-0-16">16</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">; wechat pay balance </span></span><span class="line"><span class="cl">0000-00-00 document Assets:Digital:Wechat:Cash &#34;./path/to/your_wechat_bills.csv&#34; </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Digital:Wechat:Cash &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; alipay balance </span></span><span class="line"><span class="cl">0000-00-00 document Assets:Digital:Alipay:Cash &#34;./path/to/your_alipay_bills.csv&#34; </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Digital:Alipay:Cash &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">; bank balance </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:ICBC:Savings &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:ICBC:SocialSecurity &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:BOCOM:Savings &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:BOC:Savings &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Liabilities:CreditCard:CN:ICBC &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">1111-11-19 balance Liabilities:CreditCard:CN:CMB &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">1111-11-20 balance Liabilities:CreditCard:CN:BOCOM &lt;balance_value&gt; ~ 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div></details> <h3 id="导入微信支付账单">导入微信支付账单</h3> <p>先查看上月微信 beancount 账单,确定最后导入日。</p> <p>在<a href="https://blog.triplez.cn/posts/bills-export-methods/#%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98">「微信」中导出</a>从最后导入日至今日的微信支付账单。</p> <p>使用如下命令生成账单:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-1-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-1">1</a> </span><span class="lnt" id="hl-1-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-2">2</a> </span><span class="lnt" id="hl-1-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-3">3</a> </span><span class="lnt" id="hl-1-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-4">4</a> </span><span class="lnt" id="hl-1-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-1-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">double-entry-generator translate <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --config ./config/double-entry-generator/wechat.yaml <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --provider wechat <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --output tmp-wechat.beancount <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> your_wechat_bills.csv </span></span></code></pre></td></tr></table> </div> </div><p>修改相关 <code>FIXME</code> 账户交易(posting)。</p> <p>在当月 <code>index.beancount</code> 中添加微信支付的相关 <code>balance</code> 和 <code>document</code> 语句,例:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-2-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-1">1</a> </span><span class="lnt" id="hl-2-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-2-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">0000-00-00 document Assets:Digital:Wechat:Cash &#34;./path/to/your_wechat_bills.csv&#34; </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Digital:Wechat:Cash &lt;balance_value&gt; ~ 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div><blockquote> <p><code>balance</code> 日期必须为 <code>T+1</code>,否则语句会忽略今日交易,导致对账失败。</p> <p>若对账失败,特别是小额差异,极有可能是「零钱通」发放利息所致。值得注意的是,微信「零钱通」的利息发放并不包括在微信导出的账单当中。</p> <p>值得注意的是,从「理财通」转入至「银行卡」的交易,微信支付是没有记录的,见<a href="https://github.com/deb-sig/double-entry-generator/issues/29#issuecomment-888331269">这里</a>。</p> </blockquote> <h3 id="导入支付宝账单">导入支付宝账单</h3> <p>先查看上月支付宝 beancount 账单,确定最后导入日。</p> <p>在<a href="https://blog.triplez.cn/posts/bills-export-methods/#%E6%94%AF%E4%BB%98%E5%AE%9D">「支付宝」导出</a>从最后导入日至今日的支付宝账单。</p> <p>使用如下命令生成账单:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-3-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-1">1</a> </span><span class="lnt" id="hl-3-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-2">2</a> </span><span class="lnt" id="hl-3-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-3">3</a> </span><span class="lnt" id="hl-3-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-4">4</a> </span><span class="lnt" id="hl-3-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-3-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">double-entry-generator translate <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --config ./config/double-entry-generator/alipay.yaml <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --provider alipay <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --output tmp-alipay.beancount <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> your_alipay_bills.csv </span></span></code></pre></td></tr></table> </div> </div><p>修改相关 <code>FIXME</code> 账户交易(posting)。</p> <p>在当月 <code>index.beancount</code> 中添加支付宝的相关 <code>balance</code> 和 <code>document</code> 语句,例:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-4-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-1">1</a> </span><span class="lnt" id="hl-4-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-4-2">2</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">0000-00-00 document Assets:Digital:Alipay:Cash &#34;./path/to/your_alipay_bills.csv&#34; </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Digital:Alipay:Cash &lt;balance_value&gt; ~ 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div><blockquote> <p><code>balance</code> 日期必须为 <code>T+1</code>,否则语句会忽略今日交易,导致对账失败。</p> </blockquote> <h3 id="导入火币账单">导入火币账单</h3> <p>(可选,依据月度是否有火币交易)</p> <p>先查看上月火币 beancount 账单,确定最后导入日。</p> <p>在<a href="https://blog.triplez.cn/posts/bills-export-methods/#%E7%81%AB%E5%B8%81-global">「火币」导出</a>从最后导入日至今日的火币账单。</p> <p>使用如下命令生成账单:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-5-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-1">1</a> </span><span class="lnt" id="hl-5-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-2">2</a> </span><span class="lnt" id="hl-5-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-3">3</a> </span><span class="lnt" id="hl-5-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-4">4</a> </span><span class="lnt" id="hl-5-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-5-5">5</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">double-entry-generator translate <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --config ./config/double-entry-generator/huobi.yaml <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --provider huobi <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --output tmp-huobi.beancount <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> your_huobi_bills.csv </span></span></code></pre></td></tr></table> </div> </div><p>修改相关 <code>FIXME</code> 账户交易(posting)。</p> <h3 id="导入中国工商银行账单">导入中国工商银行账单</h3> <p>先查看上月工商银行 beancount 账单,确定最后导入日。</p> <p>在<a href="https://blog.triplez.cn/posts/bills-export-methods/#%E4%B8%AD%E5%9B%BD%E5%B7%A5%E5%95%86%E9%93%B6%E8%A1%8C">「中国工商银行」导出</a>从最后导入日至今日中国工商银行的借记卡和信用卡账单。</p> <p>使用如下命令生成账单:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-6-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-1"> 1</a> </span><span class="lnt" id="hl-6-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-2"> 2</a> </span><span class="lnt" id="hl-6-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-3"> 3</a> </span><span class="lnt" id="hl-6-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-4"> 4</a> </span><span class="lnt" id="hl-6-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-5"> 5</a> </span><span class="lnt" id="hl-6-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-6"> 6</a> </span><span class="lnt" id="hl-6-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-7"> 7</a> </span><span class="lnt" id="hl-6-8"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-8"> 8</a> </span><span class="lnt" id="hl-6-9"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-9"> 9</a> </span><span class="lnt" id="hl-6-10"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-10">10</a> </span><span class="lnt" id="hl-6-11"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-11">11</a> </span><span class="lnt" id="hl-6-12"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-12">12</a> </span><span class="lnt" id="hl-6-13"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-13">13</a> </span><span class="lnt" id="hl-6-14"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-14">14</a> </span><span class="lnt" id="hl-6-15"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-15">15</a> </span><span class="lnt" id="hl-6-16"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-16">16</a> </span><span class="lnt" id="hl-6-17"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-6-17">17</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 生成贷记卡账单</span> </span></span><span class="line"><span class="cl">double-entry-generator translate <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --config ./config/double-entry-generator/icbc-1120-5595.yaml <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --provider icbc <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --output tmp-icbc-1120-5595.beancount <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> your_icbc_1120_5595_bills.csv </span></span><span class="line"><span class="cl"><span class="c1"># 生成借记卡账单</span> </span></span><span class="line"><span class="cl">double-entry-generator translate <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --config ./config/double-entry-generator/icbc-9855.yaml <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --provider icbc <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --output tmp-icbc-9855.beancount <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> your_icbc_9855_bills.csv </span></span><span class="line"><span class="cl">double-entry-generator translate <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --config ./config/double-entry-generator/icbc-5868.yaml <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --provider icbc <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> --output tmp-icbc-5868.beancount <span class="se">\ </span></span></span><span class="line"><span class="cl"><span class="se"></span> your_icbc_5868_bills.csv </span></span></code></pre></td></tr></table> </div> </div><p>修改相关 <code>FIXME</code> 账户交易(posting)。</p> <p>在当月 <code>index.beancount</code> 中添加中国工商银行的相关 <code>balance</code> 和 <code>document</code> 语句,例:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-7-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-1">1</a> </span><span class="lnt" id="hl-7-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-2">2</a> </span><span class="lnt" id="hl-7-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-3">3</a> </span><span class="lnt" id="hl-7-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-7-4">4</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">0000-00-00 document Assets:Bank:CN:ICBC:Savings &#34;./path/to/your_icbc_debit_bills.csv&#34; </span></span><span class="line"><span class="cl">0000-00-00 document Liabilities:CreditCard:CN:ICBC &#34;./path/to/your_icbc_credit_bills.csv&#34; </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:ICBC:Savings &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Liabilities:CreditCard:CN:ICBC &lt;balance_value&gt; ~ 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div><h3 id="导入其他银行卡账单">导入其他银行卡账单</h3> <p><code>double-entry-generator</code> 目前仅支持中国工商银行账单的转换,鉴于个人使用银行卡的交易较少(&lt;10 笔/月),可以直接使用手动记账。</p> <p>打开:</p> <ul> <li>中国工商银行</li> <li>招商银行</li> <li>中国银行</li> <li>交通银行</li> </ul> <p>先写一个 <code>T+1</code> 的 <code>balance</code> 对账语句,对相关的账户进行断言。</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt" id="hl-8-1"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-1">1</a> </span><span class="lnt" id="hl-8-2"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-2">2</a> </span><span class="lnt" id="hl-8-3"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-3">3</a> </span><span class="lnt" id="hl-8-4"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-4">4</a> </span><span class="lnt" id="hl-8-5"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-5">5</a> </span><span class="lnt" id="hl-8-6"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-6">6</a> </span><span class="lnt" id="hl-8-7"><a style="outline: none; text-decoration:none; color:inherit" href="#hl-8-7">7</a> </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:ICBC:Savings &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:ICBC:SocialSecurity &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:BOCOM:Savings &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Assets:Bank:CN:BOC:Savings &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">0000-00-01 balance Liabilities:CreditCard:CN:ICBC &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">1111-11-19 balance Liabilities:CreditCard:CN:CMB &lt;balance_value&gt; ~ 0.00 CNY </span></span><span class="line"><span class="cl">1111-11-20 balance Liabilities:CreditCard:CN:BOCOM &lt;balance_value&gt; ~ 0.00 CNY </span></span></code></pre></td></tr></table> </div> </div><blockquote> <p>笔者的信用卡账单分别是每月 1 日和 19 日出账,故将信用卡断言的日期设为当月 1 日以及 19 日。</p> <p>工商银行信用卡可以看到实时余额,而招行和交行的只能在出账当天才可获得余额。因此工行信用卡可以直接断言当日余额,而招行、交行银行卡需要断言出账日与余额。</p> <p>一般 <code>0000-00-01</code> 为记账当日 <code>T+1</code> ,而 <code>1111-11</code> 一般为记账当月的月份。</br> 🌰:若记账时间为 <code>2023-01-25</code> ,则 <code>0000-00-01</code> 应改为 <code>2023-01-26</code> ,<code>1111-11</code> 应改为 <code>2023-01</code> 。</p> </blockquote> <p>查找当月相关银行卡账单,手动记录在当月的 <code>index.beancount</code> 中。</p> <h2 id="总结">总结</h2> <p>以月为粒度,微信、支付宝账单基本都能够正常转换,整个工作流下来需要花费的时间成本在二十分钟以内。</p> <p>使用 <a href="https://github.com/deb-sig/double-entry-generator/"><code>double-entry-generator</code></a> 能够基本实现个人记账期望,可以考虑固化该工作流,以长期使用。</p> <h2 id="相关链接">相关链接</h2> <ol> <li>beancount/beancount</br> <a href="https://github.com/beancount/beancount">https://github.com/beancount/beancount</a></li> <li>beancount/fava</br> <a href="https://github.com/beancount/fava">https://github.com/beancount/fava</a></li> <li>deb-sig/double-entry-generator</br> <a href="https://github.com/deb-sig/double-entry-generator">https://github.com/deb-sig/double-entry-generator</a></li> <li>多种支付账户的账单导出及查看方法汇总</br> <a href="https://blog.triplez.cn/posts/bills-export-methods/">https://blog.triplez.cn/posts/bills-export-methods/</a></li> </ol>

2021/6/27
阅读更多

多种支付账户的账单导出及查看方法汇总

<h2 id="第三方电子支付">第三方电子支付</h2> <h3 id="微信支付">微信支付</h3> <ol> <li> <p>打开“微信”,点击“我的”,点击“支付”,进入如下界面:</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210517140448564.png#center"/> </figure> </li> <li> <p>点击“钱包”,点击右上角“账单”,点击右上角的“常见问题”。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210517140717511.png#center"/> </figure> </li> <li> <p>点击“下载账单”。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210517140740746.png#center"/> </figure> </li> <li> <p>选择“用于个人对账”。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210517140814585.png#center"/> </figure> </li> <li> <p>自行选择需要导出的日期范围,填写自己的邮箱即可。</p> </li> </ol> <h3 id="支付宝">支付宝</h3> <p>打开「支付宝」APP,在菜单栏中点击「我的」。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210619105626698.png#center"/> </figure> <p>进入「账单」,点击右上角「&hellip;」。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210619104713644.png#center"/> </figure> <p>选择「开具交易流水证明」。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210619105742606.png#center"/> </figure> <p>在「选择申请用途」中选择「用于个人对账」。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210619105952389.png#center"/> </figure> <p>选择需要的交易时间范围。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210619105910073.png#center"/> </figure> <p>填写电子邮箱后,即可获取账单。</p> <h4 id="余额宝">余额宝</h4> <p>打开<a href="https://yebprod.alipay.com/yeb/asset.htm">余额宝网站</a>,选择日期,点击“下载”即可。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210522013459796.png#center"/> </figure> <h2 id="银行">银行</h2> <h3 id="借记卡">借记卡</h3> <h4 id="中国工商银行">中国工商银行</h4> <p>登录<a href="https://mybank.icbc.com.cn/icbc/newperbank/perbank3/frame/frame_index.jsp">中国工商银行个人网上银行</a>,在「我的卡包」中选择某个银行卡,点击「明细」按钮,进入「明细查询」。</p> <blockquote> <p>macOS 需要使用 Safari 并安装工行插件才可正常登录。</p> </blockquote> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210529004103252.png#center"/> </figure> <p>选择需要的时间区间,在「下载明细格式」选择 「EXCEL 格式(.csv)」,点击「下载」后,提示“明细正在下载中,请稍后回到本交易页面查看下载结果”。稍等片刻,页面提示“您的历史明细下载已经处理成功,请您再次进入明细查询页面进行下载!”。此时重新点击「查询」,在「下载」按钮旁边会出现可下载结果的链接,点击即可下载真正的 csv 账单文件(不要忘记重新将下载格式改为 csv)。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/image-20210529005155486.png#center"/> </figure> <h4 id="交通银行">交通银行</h4> <p>手机「交通银行 APP」-&gt;查找“交易明细”-&gt;最下面“是否需要开立交易明细证明?”点击「立即申请」-&gt;「交易明细清单」-&gt;「电子版」-&gt; 选择日期-&gt;填写电子邮箱-&gt;「确认开立」-&gt;输入银行卡密码-&gt;开立成功</p> <p>PDF 为加密格式,加密密码为身份证后六位。</p> <p><del>如何开立 CSV 格式的账单?</del></p> <h4 id="中国银行">中国银行</h4> <p>手机「中国银行 APP」-&gt;「首页」-&gt;「更多」-&gt;「助手」-&gt;「交易流水打印」-&gt;「立即申请」-&gt; 选择日期 -&gt; 选择发送邮箱 -&gt; 填写电子邮箱 -&gt; 点击「申请」-&gt; 申请成功</p> <p>PDF 为加密格式,加密密码在「交易流水打印」的「申请记录」中可以找到。</p> <p><del>网上银行:</del> <del>需要安装安全输入插件,用 Chrome 登录<a href="https://ebsnew.boc.cn/boc15/login.html">中国银行网上银行</a>。</del></p> <p><del>找不到登录账号了。</del></p> <h3 id="信用卡">信用卡</h3> <h4 id="招商银行">招商银行</h4> <p>“电邮账单”获取方式:</p> <blockquote> <p>掌上生活-&gt;金融-&gt;查账单-&gt;右上角&hellip;-&gt;账单服务-&gt;账单补寄-&gt;(寄送方式改为「电邮账单」)-&gt;申请补寄</p> <p>&ndash; <a href="zsxsoft/my-beancount-scripts">zsxsoft/my-beancount-scripts</a></p> </blockquote> <p><del>如何获取 CSV 账单?</del></p> <p>登录<a href="https://pbsz.ebank.cmbchina.com/CmbBank_GenShell/UI/GenShellPC/Login/Login.aspx">网银大众版</a>或<a href="http://www.cmbchina.com/pbankwebNew/downloadPage.aspx">专业版</a>后,点【信用卡→账户管理→未出账单查询/已出账单查询】查看/下载/打印一年内的账单,带有业务受理专用章。</p> <p><strong>已出账单</strong>:点击【账单明细→点击下载财务明细】打印/下载,格式为【PDF】。 <strong>未出账单</strong>:点击页面右侧的下载或打印,格式为【Excel】。</p> <h2 id="社保深圳">社保(深圳)</h2> <p>深圳五险缴费明细查询:</p> <p>登录<a href="https://sipub.sz.gov.cn/hspms/mainFrame.jsp">深圳社保服务网站</a>,进入“查询服务”,选择“缴费信息查询”,点击“五险缴费明细查询”即可。</p> <p>深圳医保、养老个人账户余额查询:</p> <p>登录<a href="https://sipub.sz.gov.cn/hspms/mainFrame.jsp">深圳社保服务网站</a>,进入“查询服务”,选择“参保信息查询”,点击“基本医疗保险个人账户查询”即可。</p> <h2 id="公积金深圳">公积金(深圳)</h2> <p>「i 深圳 APP」,在首页点击「公积金」,进入「深圳市公积金」页面,即可看到余额。点击「账户明细」即可看到缴费情况。</p> <h2 id="加密货币">加密货币</h2> <h3 id="火币-global">火币 Global</h3> <p>登录<a href="https://www.huobi.com/">火币 Global 网站</a>,进入<a href="https://www.huobi.com/zh-cn/transac/?tab=2&amp;type=0">币币订单的成交明细</a>页面,选择合适的时间区间后,点击成交明细右上角的导出按钮即可。</p>

2021/6/19
阅读更多

欢迎 Macat !

<p>今天我家来了一位新主子,在此我隆重的向大家介绍下:他名字叫 Mac,是一只 Cat,所以也叫 Macat ~ 中文名叫麦麦 <del>我才不会说是因为我喜欢吃 McDonald 的 Big Mac</del> 哈哈哈。</p> <p>最后放一个照片给大家欣赏欣赏。</p> <figure class="align-center "> <img loading="lazy" src="https://triplez-public-1251926021.cos.ap-shanghai.myqcloud.com/picgo/P4112069-8136561.jpg#center"/> </figure>

2021/4/11
阅读更多