<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>逸思杂陈</title>
  <icon>https://www.gravatar.com/avatar/071e91e4d2b41eefac3330385d124f95</icon>
  <subtitle>人类一思考，上帝就发笑。</subtitle>
  <link href="https://blog.ponder.work/atom.xml" rel="self"/>
  
  <link href="https://blog.ponder.work/"/>
  <updated>2026-03-04T01:02:56.000Z</updated>
  <id>https://blog.ponder.work/</id>
  
  <author>
    <name>Jay.Run</name>
    <email>ruan.lj@foxmail.com</email>
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>esim 使用相关</title>
    <link href="https://blog.ponder.work/2026/03/04/esim-usage/"/>
    <id>https://blog.ponder.work/2026/03/04/esim-usage/</id>
    <published>2026-03-04T01:02:56.000Z</published>
    <updated>2026-03-04T01:02:56.000Z</updated>
    
    <content type="html"><![CDATA[<p>国行手机的 esim 功能有较多限制，使用不太方便。<br>但我们可以使用 esim 适配器，将 esim 转为实体 sim 卡。</p><span id="more"></span><h2 id="esim-适配器"><a href="#esim-适配器" class="headerlink" title="esim 适配器"></a>esim 适配器</h2><p>esim 适配器就是个 sim 卡，需要占用一个卡槽，可以通过软件将 esim 卡号写入到适配器。<br>可以写入多个 esim 号码，并且支持切换，但同时只能有一个在线。<br>适配器有不少品牌，如 estk、5ber、9esim，价格也各不相同，但是我都没怎么试过。<br>我使用的的适配器是 pdd 购买，20-30元，容量468KB。</p><h2 id="esim-套餐"><a href="#esim-套餐" class="headerlink" title="esim 套餐"></a>esim 套餐</h2><p>一般海外套餐都可以使用，我这里只用于接收短信，故使用 clubsim，成本大概50元。</p><h2 id="号码写入"><a href="#号码写入" class="headerlink" title="号码写入"></a>号码写入</h2><p>购买套餐后，运营商会提供二维码，安卓手机通过 <a href="https://gitea.angry.im/PeterCxy/OpenEUICC">OpenEUICC</a> 扫描就可以写入和管理 esim。</p><p>如果是 iphone，需要使用 esim 读卡器来管理 esim 号码。</p><h2 id="使用感受"><a href="#使用感受" class="headerlink" title="使用感受"></a>使用感受</h2><p>esim 还是挺方便的，用于接收验证码非常够用，还有些免费流量的卡。<br>但是切换 esim 号码时，可能需要切换飞行模式或者重启手机才能生效，不知是通病还是兼容性问题。</p><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><p>由于安卓操作系统（11+）的 bug，在“移动网络”中禁用 esim 适配器会无法打开。<br>需要通过 adb 清除移动网络的数据才能恢复</p><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb <span class="built_in">shell</span> pm <span class="keyword">clear</span> com.android.providers.telephony</span><br></pre></td></tr></table></figure><p>如果是小米等手机，清除过程可能会遇到adb权限相关问题<br>需要额外开启开发者选项中的“OEM解锁”和“USB调试（安全设置）”</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">adb <span class="keyword">shell</span> pm clear <span class="keyword">com</span>.android.providers.telephony</span><br><span class="line"></span><br><span class="line">Exception occurred <span class="keyword">while</span> executing <span class="string">&#x27;clear&#x27;</span>:</span><br><span class="line">java.lang.SecurityException: PID <span class="number">29160</span> does not have permission android.permission.CLEAR_APP_USER_DATA <span class="keyword">to</span> clear data of package <span class="keyword">com</span>.android.providers.telephony</span><br><span class="line">at <span class="keyword">com</span>.android.server.<span class="keyword">am</span>.ActivityManagerService.clearApplicationUserData(ActivityManagerService.jav<span class="variable">a:4101</span>)</span><br><span class="line">at <span class="keyword">com</span>.android.server.<span class="keyword">am</span>.ActivityManagerService.clearApplicationUserData(ActivityManagerService.jav<span class="variable">a:4054</span>)</span><br><span class="line">at <span class="keyword">com</span>.android.server.pm.PackageManagerShellCommand.runClear(PackageManagerShellCommand.jav<span class="variable">a:2385</span>)</span><br><span class="line">at <span class="keyword">com</span>.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.jav<span class="variable">a:289</span>)</span><br><span class="line">at <span class="keyword">com</span>.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.jav<span class="variable">a:97</span>)</span><br><span class="line">at android.os.ShellCommand.exec(ShellCommand.jav<span class="variable">a:38</span>)</span><br><span class="line">at <span class="keyword">com</span>.android.server.pm.PackageManagerService$IPackageManagerImpl.onShellCommand(PackageManagerService.jav<span class="variable">a:7022</span>)</span><br><span class="line">at android.os.Binder.shellCommand(Binder.jav<span class="variable">a:1158</span>)</span><br><span class="line">at android.os.Binder.onTransact(Binder.jav<span class="variable">a:960</span>)</span><br><span class="line">at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.jav<span class="variable">a:4729</span>)</span><br><span class="line">at <span class="keyword">com</span>.android.server.pm.PackageManagerService$IPackageManagerImpl.onTransact(PackageManagerService.jav<span class="variable">a:7006</span>)</span><br><span class="line">at android.os.Binder.execTransactInternal(Binder.jav<span class="variable">a:1433</span>)</span><br><span class="line">at android.os.Binder.execTransact(Binder.jav<span class="variable">a:1372</span>)</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://linux.do/t/topic/1276447/5">https://linux.do/t/topic/1276447/5</a></li><li><a href="https://v2.b2og.com/archives/15">https://v2.b2og.com/archives/15</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;国行手机的 esim 功能有较多限制，使用不太方便。&lt;br&gt;但我们可以使用 esim 适配器，将 esim 转为实体 sim 卡。&lt;/p&gt;</summary>
    
    
    
    <category term="工作生活" scheme="https://blog.ponder.work/categories/life/"/>
    
    
    <category term="Android" scheme="https://blog.ponder.work/tags/Android/"/>
    
    <category term="esim" scheme="https://blog.ponder.work/tags/esim/"/>
    
  </entry>
  
  <entry>
    <title>阻止 bilibili 网页自动关注</title>
    <link href="https://blog.ponder.work/2026/01/02/prevent-bilibili-auto-follow/"/>
    <id>https://blog.ponder.work/2026/01/02/prevent-bilibili-auto-follow/</id>
    <published>2026-01-02T18:32:09.000Z</published>
    <updated>2026-01-02T18:32:09.000Z</updated>
    
    <content type="html"><![CDATA[<p>B 站非常逆天，只要你使用网页端看视频，点开首页推荐的视频，观看一定时长，就有概率会触发自动关注该 UP 主。<br>之前也有怀疑是某个浏览器插件的锅，但更换浏览器依然能够触发这个问题。</p><blockquote><p>后来发现疑似是 bilibili 默认绑定了按键 <code>G</code> 关注 UP，非常容易误触发。 </p></blockquote><p>网上发现不少类似的问题，但都没有解决方案，迫于无奈只能使用自定义广告过滤规则了，之后没有再出现问题。</p><p>这里使用 uBlock origin, 其他去广告插件也类似, 进入插件设置 -&gt; 自定义静态规则，添加如下规则。<br>在视频页面禁止关注请求，不影响点开 UP 主主页进行关注。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">www.bilibili.com##+js(no-fetch-if, /api\.bilibili\.com\/x\/relation\/modify/, location.pathname.startsWith(<span class="string">&#x27;/video/&#x27;</span>))</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;B 站非常逆天，只要你使用网页端看视频，点开首页推荐的视频，观看一定时长，就有概率会触发自动关注该 UP 主。&lt;br&gt;之前也有怀疑是某个浏览器插件的锅，但更换浏览器依然能够触发这个问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;后来发现疑似是 bilibili 默认绑定了按</summary>
      
    
    
    
    <category term="工作生活" scheme="https://blog.ponder.work/categories/life/"/>
    
    
    <category term="Web" scheme="https://blog.ponder.work/tags/Web/"/>
    
    <category term="uBlock" scheme="https://blog.ponder.work/tags/uBlock/"/>
    
  </entry>
  
  <entry>
    <title>Hexo 版本更新与技术债务</title>
    <link href="https://blog.ponder.work/2026/01/02/hexo-upgrade-and-tech-debt/"/>
    <id>https://blog.ponder.work/2026/01/02/hexo-upgrade-and-tech-debt/</id>
    <published>2026-01-02T09:14:19.000Z</published>
    <updated>2026-01-02T20:49:29.000Z</updated>
    
    <content type="html"><![CDATA[<p>昨天花了些时间将 Hexo + next 的博客版本更新了下，发现不少问题。<br>博客已经很多年了，一直用的是最开始的Hexo版本，nodejs 也是 v12，next 主题也是早年源码安装的，还在上面做了修改。<br>也是早就明白需要更新了，但是觉得麻烦，就一直没动，最后变得相当麻烦。</p><span id="more"></span><h2 id="依赖更新"><a href="#依赖更新" class="headerlink" title="依赖更新"></a>依赖更新</h2><p>首先是版本升级，先将 nodejs 切换到最新 lts（v22）。<br>再尝试使用npm将 package.json 中的库自动更新，但由于版本差距太大以及很多库不维护了，更新不了。</p><p>最后只能使用 hexo init 新建个博客项目，将里面的依赖项复制到老博客里，删除对应的老依赖。<br>剩下的依赖，大部分是安装的插件，只能一个个搜索用途，看看有没有替代或者还要不要用。<br>package.json 中的其他参数，参考最新的配置文件，将无用（过时）的设置项删除。</p><p>使用 npm 安装 next 主题，将原 themes&#x2F;next 中的配置文件复制到 _config.next.yml。<br>对比 hexo 和 next 官方 _config 文件，相应修改 _config.yml 和 _config.next.yml。<br>删除 themes 中重复的主题目录。</p><p>使用 npm install 安装依赖，保证能够正常生成博客。</p><p>删除 node_modules，改用 pnpm 管理依赖，更轻量快速。<br>使用 pnpm 时可能会报错，如某个库的 <code>index.node</code> 找不到，是因为 pnpm 默认不执行库的 post install 脚本。<br>需要将这些库添加到 pnpm.onlyBuiltDependencies 中。</p><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;pnpm&quot;</span>: &#123;</span><br><span class="line">  <span class="string">&quot;onlyBuiltDependencies&quot;</span>: [</span><br><span class="line">    <span class="string">&quot;hexo-word-counter&quot;</span>,</span><br><span class="line">    <span class="string">&quot;hexo-util&quot;</span></span><br><span class="line">  ]</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>ps: </p><ul><li>总体来说 pnpm 还是存在一些兼容性问题，但大部分都不是 pnpm 本身的问题，基本上都是库的影子依赖。</li><li>大模型在这种疑难杂症上帮助很大</li></ul><h2 id="额外设置"><a href="#额外设置" class="headerlink" title="额外设置"></a>额外设置</h2><h3 id="hexo"><a href="#hexo" class="headerlink" title="hexo"></a>hexo</h3><p>修改 scaffolds 文章模板文件<br>老版本 hexo 的 scaffolds&#x2F;page.md 等文件格式与新版不同，会导致一些工具不能识别 front-matter, 需要更新。</p><p>老版本 front-matter 前面少了开头的 <code>---</code>，导致 vscode 的 hexo-util 插件不能识别 tag 等信息，调试了很久。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 老版本</span></span><br><span class="line"><span class="attr">title:</span> &#123;&#123; <span class="string">title</span> &#125;&#125;</span><br><span class="line"><span class="attr">date:</span> &#123;&#123; <span class="string">date</span> &#125;&#125;</span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="meta"></span></span><br><span class="line"><span class="comment"># 新版本</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">title:</span> &#123;&#123; <span class="string">title</span> &#125;&#125;</span><br><span class="line"><span class="attr">date:</span> &#123;&#123; <span class="string">date</span> &#125;&#125;</span><br><span class="line"><span class="meta">---</span></span><br></pre></td></tr></table></figure><h3 id="next"><a href="#next" class="headerlink" title="next"></a>next</h3><p>自定义样式和脚本，之前是通过之间修改主题源码实现，现在使用 custom_file_path 注入自定义内容。<br>需要修改 _config.next.yml 文件</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">custom_file_path:</span></span><br><span class="line">  <span class="attr">head:</span> <span class="string">source/_data/head.njk</span></span><br><span class="line">  <span class="attr">bodyEnd:</span> <span class="string">source/_data/body-end.njk</span></span><br><span class="line">  <span class="attr">style:</span> <span class="string">source/_data/styles.styl</span></span><br></pre></td></tr></table></figure><h4 id="自定义样式"><a href="#自定义样式" class="headerlink" title="自定义样式"></a>自定义样式</h4><p>next 主题的 Muse scheme 整体比较符合我的口味，但部分样式还需要微调。</p><p>source&#x2F;_data&#x2F;styles.styl</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">// 自动给文章的 toc 和正文标题编号, 不对首页编号</span><br><span class="line"><span class="selector-tag">body</span> &#123;<span class="attribute">counter-reset</span>: h1&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h2</span> &#123;<span class="attribute">counter-reset</span>: h2&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h3</span> &#123;<span class="attribute">counter-reset</span>: h3&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h4</span> &#123;<span class="attribute">counter-reset</span>: h4&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h5</span> &#123;<span class="attribute">counter-reset</span>: h5&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h6</span> &#123;<span class="attribute">counter-reset</span>: h6&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h2</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">counter-increment</span>: h1;</span><br><span class="line">  <span class="attribute">content</span>: <span class="built_in">counter</span>(h1) <span class="string">&quot;. &quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h3</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">counter-increment</span>: h2;</span><br><span class="line">  <span class="attribute">content</span>: <span class="built_in">counter</span>(h1) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h2) <span class="string">&quot; &quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h4</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">counter-increment</span>: h3;</span><br><span class="line">  <span class="attribute">content</span>: <span class="built_in">counter</span>(h1) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h2) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h3) <span class="string">&quot; &quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h5</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">counter-increment</span>: h4;</span><br><span class="line">  <span class="attribute">content</span>: <span class="built_in">counter</span>(h1) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h2) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h3) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h4) <span class="string">&quot; &quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h6</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">counter-increment</span>: h5;</span><br><span class="line">  <span class="attribute">content</span>: <span class="built_in">counter</span>(h1) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h2) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h3) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h4) <span class="string">&quot;.&quot;</span> <span class="built_in">counter</span>(h5) <span class="string">&quot; &quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.post-block</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h1</span><span class="selector-class">.nocount</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-class">.post-block</span><span class="selector-class">.post</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h2</span><span class="selector-class">.nocount</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-class">.post-block</span><span class="selector-class">.post</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h3</span><span class="selector-class">.nocount</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-class">.post-block</span><span class="selector-class">.post</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h4</span><span class="selector-class">.nocount</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-class">.post-block</span><span class="selector-class">.post</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h5</span><span class="selector-class">.nocount</span><span class="selector-pseudo">:before</span>,</span><br><span class="line"><span class="selector-class">.post-block</span><span class="selector-class">.post</span> <span class="selector-class">.post-body</span> <span class="selector-tag">h6</span><span class="selector-class">.nocount</span><span class="selector-pseudo">:before</span> &#123;</span><br><span class="line">  <span class="attribute">content</span>: <span class="string">&quot;&quot;</span>;</span><br><span class="line">  <span class="attribute">counter-increment</span>: none</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 隐藏文章 toc 中文章目录 tab 中的友链信息，站点概览中的保留</span><br><span class="line"><span class="selector-class">.sidebar</span> <span class="selector-class">.sidebar-toc-active</span> + <span class="selector-class">.sidebar-blogroll</span> &#123;</span><br><span class="line">    <span class="attribute">display</span>: none</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="只在文章页显示侧边栏"><a href="#只在文章页显示侧边栏" class="headerlink" title="只在文章页显示侧边栏"></a>只在文章页显示侧边栏</h4><p>next 主题的侧边栏逻辑有点问题， 就算设置 sidebar.display 为 post，当从文章跳到其他页面时，侧边栏并不会自动关闭，所以只能通过脚本来绕过。</p><p>source&#x2F;_data&#x2F;body-end.njk </p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line">  <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;pjax:success&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="variable constant_">CONFIG</span>.<span class="property">sidebar</span>.<span class="property">display</span> !== <span class="string">&#x27;remove&#x27;</span>) &#123;</span><br><span class="line">      <span class="keyword">const</span> hasTOC = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;.post-toc:not(.placeholder-toc)&#x27;</span>);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 当没有 TOC 且侧边栏正在显示时，隐藏侧边栏</span></span><br><span class="line">      <span class="keyword">if</span> (!hasTOC &amp;&amp; <span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">classList</span>.<span class="title function_">contains</span>(<span class="string">&#x27;sidebar-active&#x27;</span>)) &#123;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="title function_">dispatchEvent</span>(<span class="keyword">new</span> <span class="title class_">Event</span>(<span class="string">&#x27;sidebar:hide&#x27;</span>));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">&lt;/script&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="自定义重定向"><a href="#自定义重定向" class="headerlink" title="自定义重定向"></a>自定义重定向</h4><p>hexo 的文章不支持多个入口，比如我想要分类中英文都可以访问，默认情况是做不到的。</p><p>在设置了 category_map 的情况下，hexo 只会生成 programming 等英文链接，无法同时保留中文url作为入口。</p><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">category_map</span><span class="punctuation">:</span></span><br><span class="line">  <span class="attribute">编程</span><span class="punctuation">:</span> <span class="string">programming</span></span><br><span class="line">  <span class="attribute">随笔</span><span class="punctuation">:</span> <span class="string">writing</span></span><br><span class="line">  <span class="attribute">阅读</span><span class="punctuation">:</span> <span class="string">reading</span></span><br><span class="line">  <span class="attribute">工作生活</span><span class="punctuation">:</span> <span class="string">life</span></span><br></pre></td></tr></table></figure><p>所以最终通过在 404 页面添加自定义的 js 实现重定向逻辑。</p><p>source&#x2F;_data&#x2F;head.njk</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">&#123;% <span class="keyword">if</span> page.<span class="property">title</span> === <span class="string">&#x27;404 Page Not Found&#x27;</span> or page.<span class="property">type</span> === <span class="string">&#x27;404&#x27;</span> or page.<span class="property">layout</span> === <span class="string">&#x27;404&#x27;</span> %&#125;</span><br><span class="line">&lt;script&gt;</span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="comment">// 从 Hexo 配置中获取 category_map</span></span><br><span class="line">  <span class="keyword">var</span> categoryMap = &#123;&#123; config.<span class="property">category_map</span> | dump | safe &#125;&#125;;</span><br><span class="line">  <span class="keyword">var</span> currentPath = <span class="built_in">decodeURIComponent</span>(<span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">pathname</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 检查路径中是否包含分类映射的key</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> categoryMap) &#123;</span><br><span class="line">    <span class="keyword">if</span> (currentPath.<span class="title function_">includes</span>(key)) &#123;</span><br><span class="line">      <span class="keyword">var</span> targetCategory = categoryMap[key];</span><br><span class="line">      <span class="keyword">var</span> targetPath = currentPath.<span class="title function_">replace</span>(key, targetCategory);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 执行重定向</span></span><br><span class="line">      <span class="keyword">if</span> (currentPath !== targetPath) &#123;</span><br><span class="line">        <span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">replace</span>(targetPath);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br><span class="line">&lt;/script&gt;</span><br><span class="line">&#123;% endif %&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="更换-gitalk-为-utterances"><a href="#更换-gitalk-为-utterances" class="headerlink" title="更换 gitalk 为 utterances"></a>更换 gitalk 为 utterances</h4><p>原先一直使用 gitalk 作为文章的评论系统。<br>gitalk 每个新文章都需要作者登陆触发新建评论 issue，不太方便，所以现在换成 utterances。</p><p>utterances 评论插件 next 主题已内置，修改 _config.next.yml 可以开启。</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 注意先关闭 gitalk</span></span><br><span class="line"><span class="attr">utterances:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">ruanimal/ruanimal.github.io</span> <span class="comment"># Github repository owner and name</span></span><br><span class="line">  <span class="comment"># Available values: pathname | url | title | og:title</span></span><br><span class="line">  <span class="attr">issue_term:</span> <span class="string">title</span></span><br></pre></td></tr></table></figure><p>还需要在 github pages 仓库安装 utterances 应用。</p><p>自定义的域名可能不被 utterances 支持，无法登陆跳转<br>需要新建 source&#x2F;utterances.json</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;origins&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;https://blog.ponder.work&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;https://ruanimal.github.io&quot;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>如果你和我一样，博客有多个域名，副域名登陆跳转会失效，可以通过注入js修正<br>utterances 的登陆跳转是根据 canonical link，我们只要让它和当前url保持一致就行了 </p><p>在 source&#x2F;_data&#x2F;head.njk 文件添加以下内容</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">var</span> canonical = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;head &gt; link[rel=&quot;canonical&quot;]&#x27;</span>);</span><br><span class="line">  <span class="keyword">if</span> (canonical &amp;&amp; canonical.<span class="property">href</span>) &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">var</span> oldUrl = <span class="keyword">new</span> <span class="title function_">URL</span>(canonical.<span class="property">href</span>);</span><br><span class="line">      canonical.<span class="property">href</span> = <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">origin</span> + oldUrl.<span class="property">pathname</span> + oldUrl.<span class="property">search</span> + oldUrl.<span class="property">hash</span>;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">&#x27;Failed to update canonical link:&#x27;</span>, e);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h2 id="更换编辑器"><a href="#更换编辑器" class="headerlink" title="更换编辑器"></a>更换编辑器</h2><p>之前一直是用 mac 平台是 mweb 作为 hexo 的文章编辑器，实话说也非常好用。<br>但是我把主力操作系统换成 linux 之后，linux 上一直没找到与 mweb 相当的应用。<br>最终经过一番折腾，最终使用 vscode + Hexo Utils 插件作为平替。<br>同时对该插件进行来<a href="https://github.com/0x-jerry/vscode-hexo-utils/pulls/ruanimal">二开</a>，最终体验算是追平了 mweb。</p><h2 id="感受"><a href="#感受" class="headerlink" title="感受"></a>感受</h2><p>所谓技术债务，项目正常迭代的过程中是不断积累的，如果平时不跟进解决，最终在某个阶段就会爆炸，造成巨大影响。</p><p>平时迭代时，如果时间安排的过紧，我们就会倾向于采取保守的决策（能跑就不要动），最终不可避免的技术债务堆积。</p><p>当然，生活中其实也是这样吧，很多不紧急的问题，一直拖着，最终大概率也会无可挽回。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;昨天花了些时间将 Hexo + next 的博客版本更新了下，发现不少问题。&lt;br&gt;博客已经很多年了，一直用的是最开始的Hexo版本，nodejs 也是 v12，next 主题也是早年源码安装的，还在上面做了修改。&lt;br&gt;也是早就明白需要更新了，但是觉得麻烦，就一直没动，最后变得相当麻烦。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Hexo" scheme="https://blog.ponder.work/tags/Hexo/"/>
    
    <category term="随感" scheme="https://blog.ponder.work/tags/%E9%9A%8F%E6%84%9F/"/>
    
  </entry>
  
  <entry>
    <title>配置 Linux 作为主力操作系统</title>
    <link href="https://blog.ponder.work/2025/12/21/linux-as-the-only/"/>
    <id>https://blog.ponder.work/2025/12/21/linux-as-the-only/</id>
    <published>2025-12-21T21:48:00.000Z</published>
    <updated>2026-01-15T09:58:50.000Z</updated>
    
    <content type="html"><![CDATA[<p>这些年日常操作系统一直是 windows 和 macOS 交替使用，Linux 一般只作为服务器的操作系统。<br>然而，咖喱味的 Windows 11 （LTSC）用起来实在难受（平时只玩游戏，下载）<br>arm 的 macbook 虽然能效惊人，但是内存金子价格，软件也封闭（点名finder）和傲慢，实在受不了。<br>最后，还是转向 Linux，毕竟现在 wayland 基本堪用，国产软件也随着信创逐渐丰富了。</p><span id="more"></span><h2 id="发行版选择"><a href="#发行版选择" class="headerlink" title="发行版选择"></a>发行版选择</h2><p>这里选用 KDE Neon user edition，基于 Ubuntu LTS, 有以下优点</p><ol><li>Ubuntu 用户基数大，有问题容易解决</li><li>可以安装星火应用商店，方便使用国产软件</li><li>可以用到最新的 KDE 桌面环境，wayland 支持更好</li><li>KDE 远程桌面好用（服务端和客户端）</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">OS:</span> <span class="string">KDE</span> <span class="string">neon</span> <span class="string">User</span> <span class="string">Edition</span> <span class="string">x86_64</span></span><br><span class="line"><span class="attr">Host:</span> <span class="string">MS-7D99</span> <span class="string">(3.0)</span></span><br><span class="line"><span class="attr">Kernel:</span> <span class="string">Linux</span> <span class="number">6.14</span><span class="number">.0</span><span class="number">-33</span><span class="string">-generic</span></span><br><span class="line"><span class="attr">Uptime:</span> <span class="number">1</span> <span class="string">day,</span> <span class="number">19</span> <span class="string">hours,</span> <span class="number">23</span> <span class="string">mins</span></span><br><span class="line"><span class="attr">Packages:</span> <span class="number">2831</span> <span class="string">(dpkg)</span></span><br><span class="line"><span class="attr">Shell:</span> <span class="string">bash</span> <span class="number">5.2</span><span class="number">.21</span></span><br><span class="line"><span class="attr">Display (E2434I-T):</span> <span class="string">1920x1080</span> <span class="string">@</span> <span class="number">60</span> <span class="string">Hz</span> <span class="string">in</span> <span class="number">24</span><span class="string">&quot; [External]</span></span><br><span class="line"><span class="string">Display (KOS2718): 3840x2160 @ 60 Hz (as 1920x1080) in 27&quot;</span> [<span class="string">External</span>] <span class="string">*</span></span><br><span class="line"><span class="attr">DE:</span> <span class="string">KDE</span> <span class="string">Plasma</span> <span class="number">6.4</span><span class="number">.5</span></span><br><span class="line"><span class="attr">WM:</span> <span class="string">KWin</span> <span class="string">(Wayland)</span></span><br><span class="line"><span class="attr">WM Theme:</span> <span class="string">Breeze</span></span><br><span class="line"><span class="attr">Theme:</span> <span class="string">Breeze</span> <span class="string">(Light)</span> [<span class="string">Qt</span>]<span class="string">,</span> <span class="string">Breeze</span> [<span class="string">GTK2/3</span>]</span><br><span class="line"><span class="attr">Icons:</span> <span class="string">Breeze</span> [<span class="string">Qt</span>]<span class="string">,</span> <span class="string">breeze</span> [<span class="string">GTK2/3/4</span>]</span><br><span class="line"><span class="attr">Font:</span> <span class="string">微软雅黑</span> <span class="string">(10pt)</span> [<span class="string">Qt</span>]<span class="string">,</span> <span class="string">微软雅黑</span> <span class="string">(10pt)</span> [<span class="string">GTK2/3/4</span>]</span><br><span class="line"><span class="attr">Terminal:</span> <span class="string">/dev/pts/3</span></span><br><span class="line"><span class="attr">CPU:</span> <span class="string">13th</span> <span class="string">Gen</span> <span class="string">Intel(R)</span> <span class="string">Core(TM)</span> <span class="string">i5-13500</span> <span class="string">(20)</span> <span class="string">@</span> <span class="number">4.80</span> <span class="string">GHz</span></span><br><span class="line"><span class="attr">GPU 1:</span> <span class="string">NVIDIA</span> <span class="string">GeForce</span> <span class="string">RTX</span> <span class="number">4060 </span><span class="string">Ti</span> [<span class="string">Discrete</span>]</span><br><span class="line"><span class="attr">GPU 2:</span> <span class="string">Intel</span> <span class="string">AlderLake-S</span> <span class="string">GT1</span> <span class="string">@</span> <span class="number">1.55</span> <span class="string">GHz</span> [<span class="string">Integrated</span>]</span><br><span class="line"><span class="attr">Memory:</span> <span class="number">25.07</span> <span class="string">GiB</span> <span class="string">/</span> <span class="number">46.67</span> <span class="string">GiB</span> <span class="string">(54%)</span></span><br><span class="line"><span class="attr">Swap:</span> <span class="number">392.00</span> <span class="string">KiB</span> <span class="string">/</span> <span class="number">8.00</span> <span class="string">GiB</span> <span class="string">(0%)</span></span><br></pre></td></tr></table></figure><h2 id="硬件要求"><a href="#硬件要求" class="headerlink" title="硬件要求"></a>硬件要求</h2><ol><li>NVIDIA 显卡在 Linux 下对 Wayland 支持不好，容易出现卡死或者重启的情况，建议显示器连到核显</li><li>有些主板 Linux 兼容性不行，建议使用御三家主板</li></ol><h2 id="系统设置"><a href="#系统设置" class="headerlink" title="系统设置"></a>系统设置</h2><h3 id="Nvidia-显卡驱动"><a href="#Nvidia-显卡驱动" class="headerlink" title="Nvidia 显卡驱动"></a>Nvidia 显卡驱动</h3><p>显示器插到核显上，只使用 Nvidia 做计算，玩游戏也能调用到显卡。<br>使用开源服务器驱动，解决 Wayland 兼容性问题，防止玩游戏崩溃</p><ol><li>安装驱动</li></ol><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ubuntu-drivers install <span class="number">580</span>-<span class="keyword">server</span>-<span class="keyword">open</span></span><br></pre></td></tr></table></figure><ol start="2"><li>启用睡眠支持<a href="https://download.nvidia.com/XFree86/Linux-x86_64/570.172.08/README/powermanagement.html">参考</a><br>将显存内容保存，否则唤醒后程序会报错（如CUDA程序）</li></ol><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/modprobe.d/nvidia-power-management.conf</span></span><br><span class="line">options nvidia <span class="attribute">NVreg_PreserveVideoMemoryAllocations</span>=1 <span class="attribute">NVreg_TemporaryFilePath</span>=/tmp</span><br><span class="line"></span><br><span class="line">sudo update-initramfs</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="docker"><a href="#docker" class="headerlink" title="docker"></a>docker</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> pkg <span class="keyword">in</span> docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; <span class="keyword">do</span> <span class="built_in">sudo</span> apt-get remove <span class="variable">$pkg</span>; <span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Add Docker&#x27;s official GPG key:</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install ca-certificates curl</span><br><span class="line"><span class="built_in">sudo</span> install -m 0755 -d /etc/apt/keyrings</span><br><span class="line"><span class="built_in">sudo</span> curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chmod</span> a+r /etc/apt/keyrings/docker.asc</span><br><span class="line"></span><br><span class="line"><span class="comment"># Add the repository to Apt sources:</span></span><br><span class="line"><span class="built_in">echo</span> \</span><br><span class="line">  <span class="string">&quot;deb [arch=<span class="subst">$(dpkg --print-architecture)</span> signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \</span></span><br><span class="line"><span class="string">  <span class="subst">$(. /etc/os-release &amp;&amp; echo <span class="string">&quot;<span class="variable">$&#123;UBUNTU_CODENAME:-<span class="variable">$VERSION_CODENAME</span>&#125;</span>&quot;</span>)</span> stable&quot;</span> | \</span><br><span class="line">  <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/apt/sources.list.d/docker.list &gt; /dev/null</span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> usermod -aG docker <span class="variable">$USER</span></span><br><span class="line">newgrp docker  <span class="comment"># 立即生效（或注销重新登录）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># docker 中调用显卡</span></span><br><span class="line">https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html</span><br></pre></td></tr></table></figure><h3 id="ipv6-启用-EUI-64"><a href="#ipv6-启用-EUI-64" class="headerlink" title="ipv6 启用 EUI-64"></a>ipv6 启用 EUI-64</h3><p>可以让 ipv6 的ip后缀根据mac生成，可用于配置防火墙规则<br>使用形如 <code>::&lt;后缀&gt;/::ffff:ffff:ffff:ffff</code> 的掩码</p><figure class="highlight dos"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nmcli <span class="built_in">con</span> show</span><br><span class="line">nmcli <span class="built_in">con</span> modify &lt;NETWORK NAME&gt; ipv6.addr-gen-<span class="built_in">mode</span> eui64</span><br></pre></td></tr></table></figure><h3 id="启用休眠文件"><a href="#启用休眠文件" class="headerlink" title="启用休眠文件"></a>启用休眠文件</h3><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">sudo</span> su</span><br><span class="line"><span class="attribute">cd</span> /</span><br><span class="line"><span class="attribute">truncate</span> -s <span class="number">0</span> swapfile</span><br><span class="line"><span class="comment"># chattr +C swapfile  # disable COW on btrfs</span></span><br><span class="line"><span class="attribute">fallocate</span> -l <span class="number">8</span>G swapfile</span><br><span class="line"><span class="attribute">chmod</span> <span class="number">0600</span> swapfile</span><br><span class="line"><span class="attribute">mkswap</span> swapfile</span><br><span class="line"><span class="attribute">swapon</span> swapfile</span><br></pre></td></tr></table></figure><h3 id="使用英文用户文件夹"><a href="#使用英文用户文件夹" class="headerlink" title="使用英文用户文件夹"></a>使用英文用户文件夹</h3><p>用户个人文件夹（文档、下载等），默认是跟随系统语言。<br>可是中文在终端下输入不太方便，建议使用英文用户文件夹名称。<br>方法步骤：</p><ol><li>语言修改成英文，注销重新登录</li><li>打开文件管理器，确认修改为英文</li><li>语言修改回中文</li><li>打开文件管理器，保留原名称</li></ol><h3 id="无线网卡调优"><a href="#无线网卡调优" class="headerlink" title="无线网卡调优"></a>无线网卡调优</h3><p>WIFI 启用 WOL（网络唤醒）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看 WOL 支持</span></span><br><span class="line">iw phy0 wowlan show</span><br><span class="line"><span class="comment"># 开启 WOL 支持</span></span><br><span class="line"><span class="built_in">sudo</span> iw phy0 wowlan <span class="built_in">enable</span> magic-packet</span><br></pre></td></tr></table></figure><p>关闭节能模式，解决无线网口入站延迟较大</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cat</span> &lt;&lt;<span class="string">EOF &gt; /etc/NetworkManager/conf.d/wifi-powersave-off.conf</span></span><br><span class="line"><span class="string">[connection]</span></span><br><span class="line"><span class="string">wifi.powersave = 2</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者启动时设置</span></span><br><span class="line"><span class="built_in">sudo</span> iw dev wlo1 <span class="built_in">set</span> power_save off</span><br></pre></td></tr></table></figure><h3 id="睡眠调整"><a href="#睡眠调整" class="headerlink" title="睡眠调整"></a>睡眠调整</h3><p>睡眠到内存（suspend）耗电量很低，够用了，没必要开启混合休眠</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 只睡眠到内存</span></span><br><span class="line"><span class="comment"># 修改 /etc/systemd/sleep.conf</span></span><br><span class="line"><span class="attribute">AllowSuspend</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attribute">AllowHibernation</span>=<span class="literal">no</span></span><br><span class="line"><span class="attribute">AllowSuspendThenHibernate</span>=<span class="literal">no</span></span><br><span class="line"><span class="attribute">AllowHybridSleep</span>=<span class="literal">no</span></span><br><span class="line"><span class="attribute">SuspendState</span>=mem standby</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用休眠（可选）</span></span><br><span class="line">sudo systemctl mask hibernate.target hybrid-sleep.target</span><br><span class="line"></span><br><span class="line"><span class="comment"># nvida</span></span><br></pre></td></tr></table></figure><h3 id="intel-核显性能优化"><a href="#intel-核显性能优化" class="headerlink" title="intel 核显性能优化"></a>intel 核显性能优化</h3><p>10代以上 intel 核显</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># cat /etc/modprobe.d/i915.conf</span></span><br><span class="line">options i915 <span class="attribute">enable_fbc</span>=1 <span class="attribute">enable_guc</span>=2 <span class="attribute">enable_dc</span>=0</span><br><span class="line"></span><br><span class="line">sudo update-initramfs -u</span><br></pre></td></tr></table></figure><h3 id="RDP-远程桌面"><a href="#RDP-远程桌面" class="headerlink" title="RDP 远程桌面"></a>RDP 远程桌面</h3><p>服务端: 使用 KDE 默认的远程桌面 （可惜稳定性一般）<br>客户端: <code>apt install krdc</code></p><p>其他配置</p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 双屏只显示其中一个屏幕</span></span><br><span class="line"><span class="comment"># 修改 ~/.config/systemd/user/plasma-krdp_server.service</span></span><br><span class="line"><span class="attr">ExecStart=</span>/usr/bin/krdpserver --<span class="literal">monitor</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启服务</span></span><br><span class="line">systemctl --<span class="keyword">user</span> <span class="title">daemon-reload</span></span><br><span class="line">systemctl --<span class="keyword">user</span> <span class="title">restart</span> app-org.kde.krdpserver.service</span><br></pre></td></tr></table></figure><h3 id="使用新内核（可选）"><a href="#使用新内核（可选）" class="headerlink" title="使用新内核（可选）"></a>使用新内核（可选）</h3><p>发行版默认内核版本可能比较老，如有必要可以更新内核<br><strong>注意</strong>：mainline 的内核与 ubuntu-drivers 的 nvidia 显卡驱动不兼容（所以一般不推荐）</p><figure class="highlight smali"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo<span class="built_in"> add-apt-repository </span>ppa:cappelikan/ppa</span><br><span class="line">sudo apt update</span><br><span class="line">sudo apt install mainline</span><br></pre></td></tr></table></figure><h2 id="软件使用"><a href="#软件使用" class="headerlink" title="软件使用"></a>软件使用</h2><h3 id="常见应用替代方案"><a href="#常见应用替代方案" class="headerlink" title="常见应用替代方案"></a>常见应用替代方案</h3><ul><li>qq: 使用官方linux版本的flatpak打包<ul><li>flatpak install com.qq.QQ</li></ul></li><li>任务管理器<ul><li>flatpak install io.missioncenter.MissionCenter</li></ul></li><li>词典：使用欧陆词典<ul><li>flatpak install net.eudic.dict</li></ul></li><li>微信<ul><li>使用星火应用商店打包的官方linux版本</li></ul></li><li>下载管理器：<ul><li>motrix：普通http下载</li><li>qbittorrent： bt下载</li></ul></li><li>虚拟机<ul><li>KVM：性能更好</li><li>VMware：易用性更好</li></ul></li><li>游戏<ul><li>steam：大部分游戏都可以直接运行，性能损耗也不大</li></ul></li><li>视频播放<ul><li>haruna：使用 flatpak 版本</li></ul></li></ul><h3 id="KVM-虚拟机"><a href="#KVM-虚拟机" class="headerlink" title="KVM 虚拟机"></a>KVM 虚拟机</h3><p>安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install qemu-kvm libvirt-daemon-system virt-manager</span><br><span class="line"><span class="built_in">sudo</span> usermod -aG libvirt,kvm <span class="variable">$USER</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> --now libvirtd</span><br></pre></td></tr></table></figure><p>硬盘直通(scsi), 相比下面的方法性能更好</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">disk</span> <span class="attr">type</span>=<span class="string">&#x27;block&#x27;</span> <span class="attr">device</span>=<span class="string">&#x27;disk&#x27;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">driver</span> <span class="attr">name</span>=<span class="string">&#x27;qemu&#x27;</span> <span class="attr">type</span>=<span class="string">&#x27;raw&#x27;</span> <span class="attr">cache</span>=<span class="string">&#x27;none&#x27;</span> <span class="attr">io</span>=<span class="string">&#x27;native&#x27;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">source</span> <span class="attr">dev</span>=<span class="string">&#x27;/dev/disk/by-id/ata-TOSHIBA&#x27;</span> <span class="attr">index</span>=<span class="string">&#x27;2&#x27;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">target</span> <span class="attr">dev</span>=<span class="string">&#x27;sdg&#x27;</span> <span class="attr">bus</span>=<span class="string">&#x27;scsi&#x27;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">disk</span>&gt;</span></span><br></pre></td></tr></table></figure><p>硬盘直通（virtio），samba分享时可能卡顿</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;disk type<span class="operator">=</span><span class="string">&quot;block&quot;</span> device<span class="operator">=</span><span class="string">&quot;disk&quot;</span>&gt;</span><br><span class="line">  &lt;driver name<span class="operator">=</span><span class="string">&quot;qemu&quot;</span> type<span class="operator">=</span><span class="string">&quot;raw&quot;</span> cache<span class="operator">=</span><span class="string">&quot;none&quot;</span>/&gt;</span><br><span class="line">  &lt;source dev<span class="operator">=</span><span class="string">&quot;/dev/disk/by-id/ata-ST3000DM008&quot;</span>/&gt;</span><br><span class="line">  &lt;target dev<span class="operator">=</span><span class="string">&quot;sdc&quot;</span> bus<span class="operator">=</span><span class="string">&quot;virtio&quot;</span>/&gt;</span><br><span class="line">&lt;/disk&gt;</span><br></pre></td></tr></table></figure><p>无线网口不支持桥接，使用路由绕过</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 修改 /etc/sysctl.conf; sudo sysctl -p 生效</span></span><br><span class="line">net.ipv4.ip_forward = 1</span><br><span class="line">net.ipv4.conf.wlo1.proxy_arp=1</span><br><span class="line">net.ipv4.conf.br-routed.proxy_arp=1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建网桥和路由</span></span><br><span class="line"><span class="comment">#/etc/systemd/system/bridge-routed.service</span></span><br><span class="line">[Unit]</span><br><span class="line">Description=Create routed bridge br-routed</span><br><span class="line">After=network.target</span><br><span class="line">Wants=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=oneshot</span><br><span class="line">RemainAfterExit=<span class="built_in">yes</span></span><br><span class="line">ExecStartPre=/usr/bin/ip <span class="built_in">link</span> add br-routed <span class="built_in">type</span> bridge</span><br><span class="line">ExecStart=/usr/bin/ip <span class="built_in">link</span> <span class="built_in">set</span> br-routed up</span><br><span class="line">ExecStartPost=/usr/bin/ip route add 192.168.1.41/32 dev br-routed</span><br><span class="line">ExecStop=/usr/bin/ip <span class="built_in">link</span> delete br-routed</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改 kvm 配置</span></span><br><span class="line">&lt;interface <span class="built_in">type</span>=<span class="string">&quot;bridge&quot;</span>&gt;</span><br><span class="line">  &lt;mac address=<span class="string">&quot;00:00:00:00:00:00&quot;</span>/&gt;</span><br><span class="line">  &lt;<span class="built_in">source</span> bridge=<span class="string">&quot;br-routed&quot;</span>/&gt;</span><br><span class="line">  &lt;target dev=<span class="string">&quot;vnet0&quot;</span>/&gt;</span><br><span class="line">  &lt;model <span class="built_in">type</span>=<span class="string">&quot;virtio&quot;</span>/&gt;</span><br><span class="line">  &lt;<span class="built_in">alias</span> name=<span class="string">&quot;net0&quot;</span>/&gt;</span><br><span class="line">  &lt;address <span class="built_in">type</span>=<span class="string">&quot;pci&quot;</span> domain=<span class="string">&quot;0x0000&quot;</span> bus=<span class="string">&quot;0x0a&quot;</span> slot=<span class="string">&quot;0x00&quot;</span> <span class="keyword">function</span>=<span class="string">&quot;0x0&quot;</span>/&gt;</span><br><span class="line">&lt;/interface&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 虚拟机使用静态ip</span></span><br><span class="line">   IPv4 地址 . . . . . . . . . . . . : 192.168.1.41(首选)</span><br><span class="line">   子网掩码  . . . . . . . . . . . . : 255.255.255.0</span><br><span class="line">   默认网关. . . . . . . . . . . . . : 192.168.1.1</span><br><span class="line">   DNS 服务器  . . . . . . . . . . . : 192.168.1.1</span><br></pre></td></tr></table></figure><h3 id="dolphin-右键菜单自定义快捷方式"><a href="#dolphin-右键菜单自定义快捷方式" class="headerlink" title="dolphin 右键菜单自定义快捷方式"></a>dolphin 右键菜单自定义快捷方式</h3><p>支持文件和文件夹</p><p>chmod +  ~&#x2F;.local&#x2F;share&#x2F;kio&#x2F;servicemenus&#x2F;mklink.desktop</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Desktop Entry]</span></span><br><span class="line"><span class="attr">Type</span>=Service</span><br><span class="line"><span class="attr">ServiceTypes</span>=KonqPopupMenu/Plugin</span><br><span class="line"><span class="attr">MimeType</span>=inode/directory<span class="comment">;application/octet-stream;</span></span><br><span class="line"><span class="attr">Actions</span>=RunMyScript</span><br><span class="line"></span><br><span class="line"><span class="section">[Desktop Action RunMyScript]</span></span><br><span class="line"><span class="attr">Name</span>=制作符号链接</span><br><span class="line"><span class="attr">Icon</span>=emblem-symbolic-link</span><br><span class="line"><span class="attr">Exec</span>=/home/rlj/scripts/mklink.sh <span class="string">&quot;%F&quot;</span></span><br></pre></td></tr></table></figure><p>mklink.sh</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line"></span><br><span class="line">NAME=$(<span class="built_in">basename</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span>)</span><br><span class="line"><span class="built_in">ln</span> -s <span class="string">&quot;<span class="variable">$NAME</span>&quot;</span> <span class="string">&quot;<span class="variable">$NAME</span>.lnk&quot;</span></span><br></pre></td></tr></table></figure><h3 id="haruna-视频播放器"><a href="#haruna-视频播放器" class="headerlink" title="haruna 视频播放器"></a>haruna 视频播放器</h3><p>haruna 是 mpv 播放器的前端，功能比较齐全</p><p>存在的问题:</p><ul><li>视频播放器首次最大化窗口，总是跳到另一个屏幕<ul><li>关闭另一个屏幕，关闭应用，再打开，再最大化，再重新打开屏幕</li></ul></li><li>无法重命名文件<ul><li>设置 flatpak 目录权限</li></ul></li></ul><h3 id="flatpak-使用"><a href="#flatpak-使用" class="headerlink" title="flatpak 使用"></a>flatpak 使用</h3><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 换源</span></span><br><span class="line">flatpak remote-modify flathub <span class="params">--url=https</span>:<span class="string">//mirrors.ustc.edu.cn/flathub</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 目录授权，解决应用无法编辑文件</span></span><br><span class="line">flatpak override <span class="params">--user</span> org.kde.haruna <span class="params">--filesystem=/share</span></span><br></pre></td></tr></table></figure><h3 id="挂载-SMB-共享支持-windows-符号链接"><a href="#挂载-SMB-共享支持-windows-符号链接" class="headerlink" title="挂载 SMB 共享支持 windows 符号链接"></a>挂载 SMB 共享支持 windows 符号链接</h3><p>支持原生符号链接（目录连接），双向操作都能同步</p><p>步骤</p><ul><li><p>windows 允许符号链接<br>计算机配置 → 管理模板 → 系统 → 文件系统 → 启用 Win32 长路径<br>计算机配置 → windwows设置 → 安全设置 → 本地策略 → 用户权限分配 → 创建符号链接</p></li><li><p>samba 挂载选项<br>symlink&#x3D;native</p></li><li><p>链接时使用相对路径（两个系统绝对路径不一样）</p></li></ul><h2 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h2><h3 id="故障排查方法"><a href="#故障排查方法" class="headerlink" title="故障排查方法"></a>故障排查方法</h3><ol><li>查看系统日志 journalctl -k -b0</li><li>查看用户日志，比如应用崩溃 journalctl –user -b0</li><li>用 AI 搜索相关问题，如果无法解决再尝试 google 搜索</li></ol><h3 id="edge-无法启动"><a href="#edge-无法启动" class="headerlink" title="edge 无法启动"></a>edge 无法启动</h3><p>edge 异常退出后，有概率无法启动</p><p>报错</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Microsoft</span> Edge 进程似乎正在使用此用户配置。</span><br><span class="line">Microsoft Edge 已锁定此用户配置以防止损坏。如果你确定没有其他进程正在使用此用户配置，可以将其解锁并重新启动 Microsoft Edge。</span><br></pre></td></tr></table></figure><p>处理方法，其他 chromium 内核浏览器可能也类似</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">rm</span> ~/.config/microsoft-edge/SingletonLock</span><br></pre></td></tr></table></figure><h3 id="steam-玩游戏卡住"><a href="#steam-玩游戏卡住" class="headerlink" title="steam 玩游戏卡住"></a>steam 玩游戏卡住</h3><p>dmesg 信息</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">7</span>月 <span class="number">27</span> <span class="number">10</span>:<span class="number">48</span>:<span class="number">59</span> neon kernel: x86/split lock detection: #AC: CPU <span class="number">0</span>/KVM/<span class="number">88279</span> took a split_lock trap at address: <span class="number">0</span>xfffff80104613be8</span><br><span class="line"><span class="attribute">7</span>月 <span class="number">27</span> <span class="number">10</span>:<span class="number">51</span>:<span class="number">00</span> neon kernel: x86/split lock detection: #AC: CPU <span class="number">2</span>/KVM/<span class="number">88281</span> took a split_lock trap at address: <span class="number">0</span>xfffff8010465701c</span><br><span class="line"><span class="attribute">7</span>月 <span class="number">27</span> <span class="number">10</span>:<span class="number">52</span>:<span class="number">51</span> neon kernel: x86/split lock detection: #AC: CHTTPClientThre/<span class="number">90929</span> took a split_lock trap at address: <span class="number">0</span>xf098fc4f</span><br><span class="line"><span class="attribute">7</span>月 <span class="number">27</span> <span class="number">10</span>:<span class="number">53</span>:<span class="number">07</span> neon kernel: x86/split lock detection: #AC: CPU <span class="number">1</span>/KVM/<span class="number">88280</span> took a split_lock trap at address: <span class="number">0</span>xfffff8010465701c</span><br></pre></td></tr></table></figure><p>解决方法</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑 /etc/default/grub</span></span><br><span class="line">GRUB_CMDLINE_LINUX_DEFAULT=<span class="string">&#x27;quiet splash split_lock_detect=off&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> update-grub</span><br></pre></td></tr></table></figure><h3 id="迁移系统-修复引导"><a href="#迁移系统-修复引导" class="headerlink" title="迁移系统&amp;修复引导"></a>迁移系统&amp;修复引导</h3><ol><li>进入 livecd，使用 Gparted 复制系统到新硬盘</li><li>修改新硬盘分区的 &#x2F;etc&#x2F;fstab 的挂载点 uuid</li><li>重建 grub 引导</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mount /dev/sdaX /mnt/</span><br><span class="line">mount /dev/sda0 /mnt/boot/efi</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> &#123;proc,dev/sys&#125;; <span class="keyword">do</span></span><br><span class="line">  mount --<span class="built_in">bind</span> /<span class="variable">$&#123;i&#125;</span> /mnt/<span class="variable">$&#123;i&#125;</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">chroot</span> /mnt grub-install --target=x86_64-efi --efi-directory=/boot/efi</span><br></pre></td></tr></table></figure><h3 id="睡眠唤醒后很卡"><a href="#睡眠唤醒后很卡" class="headerlink" title="睡眠唤醒后很卡"></a>睡眠唤醒后很卡</h3><p>桌面很卡，cpu 频率上不来，只有 800mhz。<br>映泰主板兼容性问题，换主板解决</p><h3 id="终端删除文件到回收站"><a href="#终端删除文件到回收站" class="headerlink" title="终端删除文件到回收站"></a>终端删除文件到回收站</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install trash-cli</span><br><span class="line"><span class="built_in">alias</span> <span class="built_in">rm</span>=trash</span><br></pre></td></tr></table></figure><h3 id="electorn-应用无法启动"><a href="#electorn-应用无法启动" class="headerlink" title="electorn 应用无法启动"></a>electorn 应用无法启动</h3><p>报错 chrome sandbox 相关</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑 /etc/sysctl.conf 添加</span></span><br><span class="line">kernel.<span class="attribute">apparmor_restrict_unprivileged_userns</span>=0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生效</span></span><br><span class="line">sudo sysctl -p</span><br></pre></td></tr></table></figure><h3 id="登录界面隐藏无关用户"><a href="#登录界面隐藏无关用户" class="headerlink" title="登录界面隐藏无关用户"></a>登录界面隐藏无关用户</h3><p>比如某些用于文件共享的用户</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 修改 /etc/sddm.conf</span></span><br><span class="line"><span class="section">[Autologin]</span></span><br><span class="line"><span class="attr">Session</span>=plasma</span><br><span class="line"></span><br><span class="line"><span class="section">[Users]</span></span><br><span class="line"><span class="attr">HideUsers</span>=data,data-r,data-s</span><br><span class="line"><span class="attr">HideShells</span>=/usr/sbin/nologin,/bin/<span class="literal">false</span></span><br></pre></td></tr></table></figure><h3 id="偶发硬件设备工作不正常"><a href="#偶发硬件设备工作不正常" class="headerlink" title="偶发硬件设备工作不正常"></a>偶发硬件设备工作不正常</h3><p>强制重启后或者系统崩溃后，如果设备不正常，可以尝试重启到 windows 再切回 linux</p><h3 id="应用在启动器里找不到"><a href="#应用在启动器里找不到" class="headerlink" title="应用在启动器里找不到"></a>应用在启动器里找不到</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">rm</span> -f ~/.config/menus/</span><br><span class="line">kbuildsycoca6 --noincremental</span><br></pre></td></tr></table></figure><h3 id="修复睡眠唤醒问题"><a href="#修复睡眠唤醒问题" class="headerlink" title="修复睡眠唤醒问题"></a>修复睡眠唤醒问题</h3><p>睡眠之后可能出现莫名奇妙的唤醒</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看当前唤醒触发器</span></span><br><span class="line"><span class="built_in">cat</span> /proc/acpi/wakeup | grep <span class="built_in">enable</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看触发器的设备路径</span></span><br><span class="line"><span class="keyword">for</span> p <span class="keyword">in</span> /sys/class/wakeup/*/device/power/wakeup; <span class="keyword">do</span></span><br><span class="line">    dev=$(<span class="built_in">dirname</span> $(<span class="built_in">dirname</span> <span class="variable">$p</span>));</span><br><span class="line">    dev_path=$(<span class="built_in">realpath</span> <span class="variable">$dev</span>)</span><br><span class="line">    <span class="built_in">echo</span> <span class="variable">$&#123;dev_path&#125;</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 直接修改是 /proc/acpi/wakeup 是不生效的</span></span><br><span class="line"><span class="comment"># 设置具体设备的方式禁用 rtc 和 AWAC, 位置格式 $&#123;dev_path&#125;/power/wakeup</span></span><br><span class="line"><span class="comment"># 可以将禁用命令配置到 rc.local</span></span><br><span class="line"><span class="built_in">echo</span> disabled | <span class="built_in">tee</span> /sys/devices/platform/rtc_cmos/power/wakeup  <span class="comment"># rtc</span></span><br><span class="line"><span class="built_in">echo</span> disabled | <span class="built_in">tee</span> /sys/devices/platform/rtc_cmos/rtc/rtc0/alarmtimer.0.auto/power/wakeup <span class="comment"># rtc</span></span><br><span class="line"><span class="built_in">echo</span> disabled | <span class="built_in">tee</span> /sys/devices/platform/ACPI000E:00/power/wakeup  <span class="comment"># AWAC</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查是否生效</span></span><br><span class="line"><span class="built_in">cat</span> /proc/acpi/wakeup | grep <span class="built_in">enable</span></span><br></pre></td></tr></table></figure><h3 id="apt-设置代理"><a href="#apt-设置代理" class="headerlink" title="apt 设置代理"></a>apt 设置代理</h3><p>部分 apt ppa 源需要代理，但是国内的镜像源却不需要。<br>所以需要给每一个源单独设置代理的功能</p><p>有两种设置方式</p><ul><li>白名单模式（只给需要的配置）</li><li>黑名单模式（先设置默认代理， 不需要的设置直连）</li></ul><p>设置语法</p><ul><li>设置代理服务器：<code>Acquire::http::Proxy &lt;proxy-server&gt;</code></li><li>设置某个域名的代理：<code>Acquire::http::proxy::&lt;domain&gt; &lt;proxy-server|DIRECT&gt;</code></li></ul><p>示例</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">#  /etc/apt/apt.conf.d/<span class="number">99</span>proxy</span><br><span class="line"></span><br><span class="line"># 给每种协议的源设置代理，一般设置 https::Proxy 就行了</span><br><span class="line">#Acquire::http::Proxy <span class="string">&quot;http://yourproxyaddress:proxyport&quot;</span>;</span><br><span class="line">#Acquire::https::Proxy <span class="string">&quot;http://yourproxyaddress:proxyport&quot;</span>;</span><br><span class="line">#Acquire::ftp::Proxy <span class="string">&quot;http://yourproxyaddress:proxyport&quot;</span>;</span><br><span class="line">#Acquire::socks::Proxy <span class="string">&quot;http://yourproxyaddress:proxyport&quot;</span>;</span><br><span class="line"></span><br><span class="line"># 设置具体域名是否走代理</span><br><span class="line">#Acquire::http::proxy::local.mirror.address <span class="string">&quot;DIRECT&quot;</span>;</span><br><span class="line">#Acquire::http::proxy::HOST_NAME_TO_BE_PROXIED <span class="string">&quot;http://yourproxyaddress:proxyport&quot;</span></span><br><span class="line"></span><br><span class="line"># 黑名单模式示例</span><br><span class="line">Acquire::https::Proxy <span class="string">&quot;http://localhost:10808&quot;</span>;</span><br><span class="line">Acquire::https::Proxy::<span class="string">&quot;mirrors.aliyun.com&quot;</span> <span class="string">&quot;DIRECT&quot;</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="限制硬盘读写速度"><a href="#限制硬盘读写速度" class="headerlink" title="限制硬盘读写速度"></a>限制硬盘读写速度</h3><p>某些nvme硬盘的发热很严重, 如果全速运行会导致系统不稳定.<br>所以需要对硬盘速度进行限制</p><p>使用 systemd 设置限速</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建配置目录</span></span><br><span class="line">sudo mkdir -p mkdir -p /etc/systemd/system/&#123;user,system&#125;.slice.d/</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置用户限制</span></span><br><span class="line">sudo tee /etc/systemd/system/user.slice.d/io-limit.conf  &gt; /dev/null &lt;&lt;EOF</span><br><span class="line">[Slice]</span><br><span class="line">IOReadBandwidthMax=/dev/nvme0n1 800M</span><br><span class="line">IOWriteBandwidthMax=/dev/nvme0n1 300M</span><br><span class="line">IOReadIOPSMax=/dev/nvme0n1 60000</span><br><span class="line">IOWriteIOPSMax=/dev/nvme0n1 30000</span><br><span class="line"></span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">系统也使用同样的限制</span></span><br><span class="line">sudo ln -sf /etc/systemd/system/user.slice.d/io-limit.conf \</span><br><span class="line">    /etc/systemd/system/system.slice.d/</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>速度测试</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">~$ </span><span class="language-bash"><span class="built_in">dd</span> <span class="keyword">if</span>=/dev/zero of=testfile bs=1M count=2048 oflag=direct status=progress \</span></span><br><span class="line"><span class="language-bash">    &amp;&amp; <span class="built_in">rm</span>  testfile</span></span><br><span class="line">2099249152 bytes (2.1 GB, 2.0 GiB) copied, 7 s, 300 MB/s</span><br><span class="line">2048+0 records in</span><br><span class="line">2048+0 records out</span><br><span class="line">2147483648 bytes (2.1 GB, 2.0 GiB) copied, 7.16429 s, 300 MB/s</span><br></pre></td></tr></table></figure><h3 id="archlinux-命令行不能使用回收站"><a href="#archlinux-命令行不能使用回收站" class="headerlink" title="archlinux 命令行不能使用回收站"></a>archlinux 命令行不能使用回收站</h3><p>安装 cachyos 时，trash-cli 不能使用，其他应用也不能删除文件到回收站</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> pacman -S gvfs </span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;这些年日常操作系统一直是 windows 和 macOS 交替使用，Linux 一般只作为服务器的操作系统。&lt;br&gt;然而，咖喱味的 Windows 11 （LTSC）用起来实在难受（平时只玩游戏，下载）&lt;br&gt;arm 的 macbook 虽然能效惊人，但是内存金子价格，软件也封闭（点名finder）和傲慢，实在受不了。&lt;br&gt;最后，还是转向 Linux，毕竟现在 wayland 基本堪用，国产软件也随着信创逐渐丰富了。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Linux" scheme="https://blog.ponder.work/tags/Linux/"/>
    
    <category term="KDE" scheme="https://blog.ponder.work/tags/KDE/"/>
    
  </entry>
  
  <entry>
    <title>在 Linux 虚拟机中使用 PyAutoGUI 做自动化</title>
    <link href="https://blog.ponder.work/2025/05/27/pyautogui-in-linux-virtual-machine/"/>
    <id>https://blog.ponder.work/2025/05/27/pyautogui-in-linux-virtual-machine/</id>
    <published>2025-05-27T21:48:00.000Z</published>
    <updated>2025-06-11T10:11:50.000Z</updated>
    
    <content type="html"><![CDATA[<p>PyAutoGUI 是 GUI 功能强大自动化方案，但 UI 程序的运行环境选择与配置也是一大难题。</p><span id="more"></span><h2 id="系统选择"><a href="#系统选择" class="headerlink" title="系统选择"></a>系统选择</h2><p>为了让环境可迁移，免维护，资源消耗少，必须使用虚拟化的方案。<br>虽然 PyAutoGUI 支持 Windows&#x2F;macOS&#x2F;Linux 三个平台。但是各有各的弊端。</p><ul><li>Windows 的系统臃肿且开发环境不友好</li><li>macOS 的权限管理过于严格且虚拟化难度大，高dpi的屏幕也不适合自动化</li><li>Linux 就是最好的选择了，但执行的应用可能不支持 Linux，必须引入 Wine</li></ul><p>综合以上情况考虑，系统环境选择如下</p><ul><li>pve: 宿主机系统，也可以选择别的环境</li><li>debian: 客户机系统，目前是 debian 12, 方便使用 deepin 的 wine 程序</li><li>mate: 桌面环境，比较轻量，实测兼容性比 xfce 好</li><li><a href="https://gitee.com/spark-store-project/spark-store">星火应用商店</a>: 方便安装各种国内软件和 Wine 程序</li><li>统信 Windows 应用兼容引擎: 在星火应用商店中安装，可以用于安装和打包商店中没有的程序</li></ul><h2 id="虚拟机配置"><a href="#虚拟机配置" class="headerlink" title="虚拟机配置"></a>虚拟机配置</h2><p>步骤</p><ol><li>创建好 pve 虚拟机， 安装 debian，选择 mate 桌面</li><li>设置分辨率：控制中心 -&gt; 显示器 -&gt; 设置为 1920x1080</li><li>关闭自动睡眠：控制中心 -&gt; 电源管理 -&gt; 动作和显示修改为<code>从不</code></li><li>关闭屏幕保护：控制中心 -&gt; 屏幕保护程序 -&gt; 取消勾选所有选项</li><li>安装<code>gnome-screenshot</code>:  用于 PyAutoGUI 内部调用截图</li><li>安装星火应用商店（可选）</li><li>安装统信 Windows 应用兼容引擎（可选）：在星火应用商店中安装</li><li>火焰截图（可选）：在星火应用商店中安装，更方便对应用的按钮和文字截图。</li></ol><p>其他说明：</p><ul><li>如果不关闭自动睡眠和屏幕保护，PyAutoGUI 就无法截取到应用的图像，也就无法操作了。</li><li>图形显示协议默认选 x11，wayland 对显卡有要求，虚拟机不方便处理。</li></ul><h3 id="睡眠后时间不对"><a href="#睡眠后时间不对" class="headerlink" title="睡眠后时间不对"></a>睡眠后时间不对</h3><p>虚拟机睡眠唤醒后时间没有更新，不知道为什么没有触发时间更新。<br>目前只能通过强行同步来解决</p><p>在脚本里执行同步命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 和阿里云ntp服务器同步</span></span><br><span class="line"><span class="built_in">sudo</span> ntpdig -S ntp.aliyun.com</span><br></pre></td></tr></table></figure><p>或者，理论上可以通过虚拟机中的 systemd-suspend hook，或者 pve hookscript 来解决（没试成功）</p><h2 id="PyAutoGUI-使用技巧"><a href="#PyAutoGUI-使用技巧" class="headerlink" title="PyAutoGUI 使用技巧"></a>PyAutoGUI 使用技巧</h2><h3 id="远程登录调试"><a href="#远程登录调试" class="headerlink" title="远程登录调试"></a>远程登录调试</h3><p>pve 默认图片控制台不太方便，不能复制粘贴，所以使用远程登录调试<br>由于系统使用 x11 环境，这里使用 xrdp 作为远程服务端。<br>如果以后更新到 wayland，可以使用 <code>gnome-remote-desktop</code>。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install xorgxrdp xrdp</span><br></pre></td></tr></table></figure><h3 id="远程登录时找不到显示器"><a href="#远程登录时找不到显示器" class="headerlink" title="远程登录时找不到显示器"></a>远程登录时找不到显示器</h3><p>xrdp 或者 ssh 远程登录执行脚本时，窗口相关命令可能会提示找不到显示器。<br>可以在自动化脚本中设置以下环境变量</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">os<span class="selector-class">.environ</span><span class="selector-attr">[<span class="string">&#x27;DISPLAY&#x27;</span>]</span> = <span class="string">&#x27;:0&#x27;</span></span><br><span class="line">os<span class="selector-class">.environ</span><span class="selector-attr">[<span class="string">&#x27;XAUTHORITY&#x27;</span>]</span> = <span class="string">&#x27;/home/&lt;your-name&gt;/.Xauthority&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="过滤没必要的截图日志"><a href="#过滤没必要的截图日志" class="headerlink" title="过滤没必要的截图日志"></a>过滤没必要的截图日志</h3><p>gnome-screenshot 截图时会产生一些无用日志</p><figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator">**</span> Message<span class="operator">:</span> 17<span class="operator">:</span>36<span class="operator">:</span>31.888<span class="operator">:</span> Unable <span class="keyword">to</span> <span class="keyword">use</span> GNOME Shell&#x27;s </span><br><span class="line">builtin screenshot <span class="keyword">interface</span>, resorting <span class="keyword">to</span> fallback X11.</span><br></pre></td></tr></table></figure><p>新建一个 <code>gnome-screenshot</code> 文件，赋予执行权限，放到 PATH 环境变量中比 <code>/usr/bin</code> 靠前的路径</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="built_in">exec</span> /usr/bin/gnome-screenshot <span class="string">&quot;<span class="variable">$@</span>&quot;</span> &gt;&gt; /tmp/gnome-screenshot.log 2&gt;&amp;1</span><br></pre></td></tr></table></figure><h3 id="验证码识别"><a href="#验证码识别" class="headerlink" title="验证码识别"></a>验证码识别</h3><h4 id="pytesseract"><a href="#pytesseract" class="headerlink" title="pytesseract"></a>pytesseract</h4><p>常见的方案是 <code>pytesseract</code>， 但是效果不好，识别率比较一般。</p><p>安装</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt <span class="keyword">install</span> tesseract-ocr</span><br><span class="line">pip <span class="keyword">install</span> pytesseract</span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pytesseract.image_to_string(image, config=<span class="string">&#x27;--psm 8 -c tessedit_char_whitelist=0123456789&#x27;</span>)</span><br></pre></td></tr></table></figure><h4 id="ddddocr"><a href="#ddddocr" class="headerlink" title="ddddocr"></a>ddddocr</h4><p><a href="https://github.com/sml2h3/ddddocr">ddddocr</a> 是基于机器学习的验证码识别库，识别效果比较好。</p><p>这里使用 docker 安装 <a href="https://github.com/sml2h3/ddddocr-fastapi">fastapi接口</a></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">docker</span> run -d -p <span class="number">8000</span>:<span class="number">8000</span> oozzbb/ddddocr-fastapi:latest</span><br></pre></td></tr></table></figure><p>使用</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">ocr_image</span>(<span class="params">file=<span class="string">&#x27;region.png&#x27;</span></span>):</span><br><span class="line">    url = <span class="string">&#x27;http://your-address:8000/ocr&#x27;</span></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(file, <span class="string">&#x27;rb&#x27;</span>) <span class="keyword">as</span> fp:</span><br><span class="line">        files = &#123;</span><br><span class="line">            <span class="string">&#x27;file&#x27;</span>: fp,</span><br><span class="line">        &#125;</span><br><span class="line">    data = &#123;</span><br><span class="line">        <span class="string">&#x27;probability&#x27;</span>: <span class="string">&#x27;false&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;png_fix&#x27;</span>: <span class="string">&#x27;false&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;charsets&#x27;</span>: <span class="string">&#x27;0123456789&#x27;</span>  <span class="comment"># 验证码可能的字符列表</span></span><br><span class="line">    &#125;</span><br><span class="line">    response = requests.post(url, files=files, data=data)</span><br><span class="line">    <span class="keyword">assert</span> response.ok</span><br><span class="line">    <span class="keyword">return</span> response.json()[<span class="string">&#x27;data&#x27;</span>]</span><br></pre></td></tr></table></figure><h3 id="高效执行自动化脚本"><a href="#高效执行自动化脚本" class="headerlink" title="高效执行自动化脚本"></a>高效执行自动化脚本</h3><p>可以在宿主机远程调用自动化脚本，并且脚本执行完成后挂起虚拟机</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">HOST=your-name@vm-ip-address</span><br><span class="line">VMID=109   <span class="comment"># pve 虚拟机id</span></span><br><span class="line"></span><br><span class="line">qm resume <span class="variable">$VMID</span>   <span class="comment"># 唤醒虚拟机</span></span><br><span class="line"><span class="built_in">sleep</span> 2</span><br><span class="line"></span><br><span class="line">ssh <span class="variable">$HOST</span> <span class="string">&quot;SOME_VAR=foobar python your-script.py&quot;</span>  <span class="comment"># 执行脚本</span></span><br><span class="line"><span class="built_in">sleep</span> 1</span><br><span class="line"></span><br><span class="line">qm <span class="built_in">suspend</span> <span class="variable">$VMID</span>   <span class="comment"># 关闭虚拟机</span></span><br></pre></td></tr></table></figure><h3 id="其他有用的函数"><a href="#其他有用的函数" class="headerlink" title="其他有用的函数"></a>其他有用的函数</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 切换窗口到前台</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">wm_to_front</span>(<span class="params">title: <span class="built_in">str</span></span>):</span><br><span class="line">    <span class="keyword">if</span> sys.platform == <span class="string">&#x27;linux&#x27;</span>:</span><br><span class="line">        <span class="keyword">return</span> subprocess.check_call([<span class="string">&#x27;wmctrl&#x27;</span>, <span class="string">&#x27;-a&#x27;</span>, title])</span><br><span class="line">    <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"><span class="comment"># 点击或者移到到图片位置</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">click_image</span>(<span class="params">image, confidence=<span class="number">0.9</span>, grayscale=<span class="literal">True</span>, delay=<span class="number">1.5</span>, </span></span><br><span class="line"><span class="params">        duration=<span class="number">0.6</span>, offset=(<span class="params"><span class="number">1</span>, <span class="number">0</span></span>), abort=<span class="literal">True</span>, click=<span class="literal">True</span></span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        loc = pyautogui.locateOnScreen(image, confidence=confidence, grayscale=grayscale)</span><br><span class="line">    <span class="keyword">except</span> pyautogui.ImageNotFoundException:</span><br><span class="line">        loc = <span class="literal">None</span></span><br><span class="line">    <span class="keyword">if</span> loc:</span><br><span class="line">        x, y = pyautogui.center(loc)</span><br><span class="line">        <span class="keyword">if</span> click:</span><br><span class="line">            pyautogui.click(x+offset[<span class="number">0</span>], y+offset[<span class="number">1</span>], duration=duration)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            pyautogui.moveTo(x+offset[<span class="number">0</span>], y+offset[<span class="number">1</span>], duration=duration)</span><br><span class="line">        time.sleep(delay)</span><br><span class="line">        <span class="keyword">return</span> loc</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">if</span> abort:</span><br><span class="line">            <span class="keyword">raise</span> RuntimeError(<span class="string">f&quot;can not find <span class="subst">&#123;image&#125;</span>!&quot;</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待某个图片出现</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">wait_button</span>(<span class="params">image, timeout, interval=<span class="number">10</span></span>):</span><br><span class="line">    <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(timeout//interval):</span><br><span class="line">        time.sleep(interval)</span><br><span class="line">        loc = click_image(image, abort=<span class="literal">False</span>)</span><br><span class="line">        <span class="keyword">if</span> loc:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">raise</span> RuntimeError(<span class="string">f&#x27;wait <span class="subst">&#123;image&#125;</span> failed&#x27;</span>)</span><br><span class="line">    time.sleep(interval/<span class="number">2</span>)</span><br></pre></td></tr></table></figure><h2 id="使用感受"><a href="#使用感受" class="headerlink" title="使用感受"></a>使用感受</h2><p>PyAutoGUI 是基于图像而不是图型控件来识别目标，没有确认反馈。<br>相比于网页自动化的 Selenium 就感觉落后些了。<br>当然，Windows 平台有 <code>pywinauto</code> 支持控件识别，但我不咋熟悉。<br>总之，PyAutoGUI 就像手枪，虽然比较简陋，但还是很直观的，执行简单的任务也够用了。</p><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ul><li>自动化代码中不要编码用户名密码相关信息，建议用环境变量传入</li><li>不要轻易调整系统的 dpi(建议设为1.0) 和分辨率，可能导致图片无法识别</li><li>xfce 对 wine 应用的兼容性似乎不好，wine 文件保存对话框被可能被反复触发，原因不明。</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;PyAutoGUI 是 GUI 功能强大自动化方案，但 UI 程序的运行环境选择与配置也是一大难题。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Python" scheme="https://blog.ponder.work/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>语言的力量</title>
    <link href="https://blog.ponder.work/2025/05/27/the-power-of-language/"/>
    <id>https://blog.ponder.work/2025/05/27/the-power-of-language/</id>
    <published>2025-05-27T20:41:00.000Z</published>
    <updated>2025-05-28T17:08:08.000Z</updated>
    
    <content type="html"><![CDATA[<p>语言的力量远比想象的强大，某种程度上是有虚空造物的能力。</p><span id="more"></span><p>你可以凭空创造一个概念，并植入你的听众脑子，不论好还是坏。诈骗分子、PUA 大师、营销大师无不精通此等技艺。</p><p>起因是一个综艺节目，有个导演对哈妮克孜说了句话，让我惊为天人，让我惊叹“你他娘真是个人才”。<br>它在线下拍戏的时候见到了哈妮克孜，哈当时戴了副大镜框眼镜，于是它说道“我当时看到你的时候，感觉好失望，你和我银幕上看到的哈妮克孜完全不一样”。<br>它凭空创造了一个观念（事实），哈妮克孜颜值不行。怎么创造的呢？和哈妮克孜荧幕形象相比。这显然是不客观的，但很难瞬间就察觉到。</p><p>这样，好与坏、美与丑一切观念都是可以被重新定义的，只要你选择合适的目标和对照标准，甚至是你脑海里的标准。<br>你是丑的和你漂亮的时候比，你是蠢的和聪明的时候比，我对你是失望的和对你的期望相比。<br>PUA 大师就是用这种手法实现打压，新闻学就是这么颠倒黑白，当然也可以反向操作，那么你就成为知心姐姐鼓励大师。</p><p>所以，神话中的言出法随也不难理解，毕竟人是观念的动物，你改变&#x2F;植入了某人的某个观念，那么你就改变了他眼中的世界。<br>这也就是《乔布斯传》中乔布斯的扭曲现实力场。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;语言的力量远比想象的强大，某种程度上是有虚空造物的能力。&lt;/p&gt;</summary>
    
    
    
    <category term="随笔" scheme="https://blog.ponder.work/categories/writing/"/>
    
    
    <category term="随笔" scheme="https://blog.ponder.work/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
  <entry>
    <title>联想笔记本 BIOS 跳过检测强制降级</title>
    <link href="https://blog.ponder.work/2025/05/15/levono-bios-force-downgrade/"/>
    <id>https://blog.ponder.work/2025/05/15/levono-bios-force-downgrade/</id>
    <published>2025-05-15T18:00:00.000Z</published>
    <updated>2025-05-15T20:37:15.000Z</updated>
    
    <content type="html"><![CDATA[<p>联想某些版本的 bios 似乎会禁止降级，即使打开 bios 设置里的允许降级选项，依然会提示 “this platform does not support IHISI interface” 的错误，导致降级失败。</p><span id="more"></span><h2 id="降级方法"><a href="#降级方法" class="headerlink" title="降级方法"></a>降级方法</h2><p>经过一些尝试，找到了方法绕过，以 <code>ThinkBook 14 G3 ACL</code> 机型为例</p><ol><li>打开驱动<a href="https://newthink.lenovo.com.cn/driveList.html?selname=21A2004H0D">下载页面</a> </li><li>打开 <a href="https://think.lenovo.com.cn/support/driver/driverdetail.aspx?DEditid=134398">BIOS</a> 页面， 下载以下安装文件<ul><li>当前版本 BIOS 的安装包（<code>NEW</code>），版本 GQCN41WW_HFCN36WW</li><li>以及想要降级的 BIOS (<code>OLD</code>)，版本 GQCN36WW_HFCN31WW</li></ul></li><li>运行 <code>NEW</code>，选择仅解压。找到解压目录，应该只有一个 exe 程序，使用 7-zip 或者 bandizip 打开并提取内部文件。</li><li>对 <code>OLD</code> 也执行如上操作，如果没有解压选项，可以尝试直接提取。</li><li>将 <code>OLD</code> 提取目录中的 .bin 格式固件（GLV3A036.bin、GLV4D031.bin）复制到 <code>NEW</code> 提取目录中。</li><li>修改 <code>NEW</code> 提取目录中的 <code>platform.ini</code> 文件，找到如下内容。将其中的 .bin 文件名，修改为 <code>OLD</code> 中的 .bin 文件名，保存。有两个 .bin 文件，文件名是和版本中的数字对应的，不要修改错误。 <figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">; 修改前</span><br><span class="line">[MULTI_FD]</span><br><span class="line"><span class="attribute">Flag</span>=1</span><br><span class="line">FD#<span class="attribute">01</span>=ID,GLV4D,GLV4D036.bin</span><br><span class="line">FD#<span class="attribute">02</span>=PCI,0,1,1,0,FFFFFFFF,FFFFFFFF,GLV3A041.bin</span><br><span class="line"></span><br><span class="line">; 修改后</span><br><span class="line">[MULTI_FD]</span><br><span class="line"><span class="attribute">Flag</span>=1</span><br><span class="line">FD#<span class="attribute">01</span>=ID,GLV4D,GLV4D031.bin</span><br><span class="line">FD#<span class="attribute">02</span>=PCI,0,1,1,0,FFFFFFFF,FFFFFFFF,GLV3A036.bin</span><br></pre></td></tr></table></figure></li><li>运行 <code>NEW</code> 提取目录中的 H2OFFT-Wx64.exe，等待降级完成。</li></ol><p><strong>注意：有风险，请谨慎操作。不要跨太多版本，可能有不可预料的问题</strong></p><p><img data-src="https://image.ponder.work/mweb/2025-05-15---17473119676089.jpg"></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.geektech.co.nz/lenovo-y700-how-to-enable-bios-downgrade">https://www.geektech.co.nz/lenovo-y700-how-to-enable-bios-downgrade</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;联想某些版本的 bios 似乎会禁止降级，即使打开 bios 设置里的允许降级选项，依然会提示 “this platform does not support IHISI interface” 的错误，导致降级失败。&lt;/p&gt;</summary>
    
    
    
    <category term="工作生活" scheme="https://blog.ponder.work/categories/life/"/>
    
    
    <category term="硬件" scheme="https://blog.ponder.work/tags/%E7%A1%AC%E4%BB%B6/"/>
    
  </entry>
  
  <entry>
    <title>redroid “设备未获得play保护机制认证” 问题</title>
    <link href="https://blog.ponder.work/2025/05/07/redroid-google-play-login/"/>
    <id>https://blog.ponder.work/2025/05/07/redroid-google-play-login/</id>
    <published>2025-05-07T18:00:00.000Z</published>
    <updated>2025-05-08T15:05:34.000Z</updated>
    
    <content type="html"><![CDATA[<p>使用 redroid 等安卓虚拟环境，可能会发现 google play 用不了的问题。<br>虽然系统集成了 gapps，但系统提示 “设备未获得play保护机制认证”，无法登录 play 商店。<br>可能的原因比较多，这里大概是因为虚拟机的型号没在google的数据库里。<br>解决方案就是，获取 GSF ID 注册到 Google。</p><span id="more"></span><p><img data-src="https://image.ponder.work/mweb/2025-05-08---17466861942743.jpg"></p><h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol><li>安装 <a href="https://apkpure.com/device-id/vtechnotm.com.deviceid">device id</a></li><li>打开应用，复制 GSF id，假设为<code>ffffffff</code></li><li>终端运行<code>printf &quot;%d\n&quot; 0xffffffff</code>，转换换成10进制数字。</li><li>打开 <a href="https://www.google.com/android/uncertified/">https://www.google.com/android/uncertified/</a> 输入转换后的结果并提交</li></ol><p><img data-src="https://image.ponder.work/mweb/2025-05-08---17466877549957.jpg"> </p>]]></content>
    
    
    <summary type="html">&lt;p&gt;使用 redroid 等安卓虚拟环境，可能会发现 google play 用不了的问题。&lt;br&gt;虽然系统集成了 gapps，但系统提示 “设备未获得play保护机制认证”，无法登录 play 商店。&lt;br&gt;可能的原因比较多，这里大概是因为虚拟机的型号没在google的数据库里。&lt;br&gt;解决方案就是，获取 GSF ID 注册到 Google。&lt;/p&gt;</summary>
    
    
    
    <category term="工作生活" scheme="https://blog.ponder.work/categories/life/"/>
    
    
    <category term="Android" scheme="https://blog.ponder.work/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>在 iOS 上访问安卓应用</title>
    <link href="https://blog.ponder.work/2025/04/26/access-android-apps-on-ios/"/>
    <id>https://blog.ponder.work/2025/04/26/access-android-apps-on-ios/</id>
    <published>2025-04-26T18:00:00.000Z</published>
    <updated>2025-04-27T14:53:17.000Z</updated>
    
    <content type="html"><![CDATA[<p>有些应用在安卓上是独占的，iOS 上又没有比较好的替代品，而且 iOS 上没有能用安卓模拟器。<br>如果使用多个设备，维护的心智成本又高，被这个问题困扰了许久。</p><p>最近碰巧了解了 <a href="https://github.com/Genymobile/scrcpy">scrcpy</a>, 用于远程控制安卓，终于解决了这个问题。</p><span id="more"></span><p>需要的工具</p><ol><li>安卓环境：虚拟环境或者物理设备均可，这里使用虚拟环境。以下是可选的虚拟环境</li></ol><ul><li>PVE 的 PCT容器 (Proxmox Container Toolkit)，需要打<a href="https://github.com/lurenJBD/PCT-patches">补丁</a></li><li>docker，基于<a href="https://github.com/remote-android/redroid-doc">Redroid</a> 需要调整内核参数</li><li>WSA (Windows Subsystem for Android)，与 scrcpy 配合有点问题，窗口有黑边。</li><li>VMware、VirtualBox等虚拟化平台，需要解决虚拟显卡。</li></ul><ol start="2"><li>使用 <a href="https://github.com/wsvn53/scrcpy-mobile">scrcpy-mobile</a> 远程控制安卓环境, 当然 scrcpy 也支持 Windows、Linux、macOS</li></ol><h2 id="PCT-安卓容器"><a href="#PCT-安卓容器" class="headerlink" title="PCT 安卓容器"></a>PCT 安卓容器</h2><p>由于已有PVE环境，这里选用PCT，主要步骤</p><ol><li>根据 <a href="https://github.com/lurenJBD/PCT-patches">PCT-patches</a> 文档，给 PCT 打好补丁</li><li>根据 <a href="https://github.com/lurenJBD/PCT-patches/releases/download/lineage/lineage19.1-x86_64-houdini-magisk-gapps.tar.gz">lineageOS 模板</a>，新建安卓容器，注意去除Unprivileged container的勾选。</li><li>修改 lxc.init.cmd 选项，在后面增加以下参数，调整分辨率和iPad一致。 <figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">androidboot.<span class="attribute">redroid_width</span>=1668 androidboot.<span class="attribute">redroid_height</span>=2388 androidboot.<span class="attribute">redroid_fps</span>=60</span><br></pre></td></tr></table></figure></li></ol><p>ps: 安卓容器对宿主机性能似乎有一定要求，J4125 只是勉强够用，流畅度一般。</p><h2 id="macOS-中使用-scrcpy"><a href="#macOS-中使用-scrcpy" class="headerlink" title="macOS 中使用 scrcpy"></a>macOS 中使用 scrcpy</h2><p>安装 <a href="https://github.com/Genymobile/scrcpy">scrcpy</a> ，连接命令</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">scrcpy <span class="attribute">--audio-codec</span>=aac \</span><br><span class="line">  <span class="attribute">--video-codec</span>=h264 \</span><br><span class="line">  <span class="attribute">--video-bit-rate</span>=16M \</span><br><span class="line">  <span class="attribute">--max-fps</span>=60 \</span><br><span class="line">  <span class="attribute">--tcpip</span>=192.168.10.181:5555 \</span><br><span class="line">  <span class="attribute">--start-app</span>=io.legado.app.release</span><br></pre></td></tr></table></figure><p>参数解释</p><ul><li><code>--audio-codec=aac</code>同步声音，使用 AAC 编码，默认参数可能导致没有声音</li><li><code>--video-codec=h264</code>使用 H.264 视频压缩编码</li><li><code>--video-bit-rate=16M</code>16Mbps 码率，高画质</li><li><code>--max-fps=60</code>最大帧率 60FPS，画面流畅</li><li><code>--tcpip=192.168.10.181:5555</code>Wi-Fi adb 连接设备</li><li><code>--start-app=io.legado.app.release</code> （可选）连接后直接启动 Legado App，应用列表可使用<code>adb shell pm list packages -3</code>查看</li></ul><h2 id="iOS-中使用-scrcpy-mobile"><a href="#iOS-中使用-scrcpy-mobile" class="headerlink" title="iOS 中使用 scrcpy-mobile"></a>iOS 中使用 scrcpy-mobile</h2><p>appstore中安装 <a href="https://apps.apple.com/cn/app/scrcpy-remote/id1629352527">scrcpy-mobile</a></p><p>由于该应用的用户界面易用性比较差，表单也不支持有些 scrcpy 参数，这里直接使用<code>快捷指令</code>打开应用。<br>设置打开 scrcpy-mobile 后，开启<code>引导式访问</code>，防止误触。同时可以在 iOS 设置中开启引导式访问的<code>面容id</code>，防止频繁输入密码。</p><p><img data-src="https://image.ponder.work/mweb/2025-04-27---17457324901465.jpg"></p><p>scrcpy-mobile 的 url schema</p><figure class="highlight sas"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scrcpy2://192.168.10.181:5555?enable-audio=true<span class="variable">&amp;audio</span>-codec=aac<span class="variable">&amp;video</span>-bit-rate=16M<span class="variable">&amp;video</span>-codec=h264<span class="variable">&amp;max</span>-fps=60</span><br></pre></td></tr></table></figure><p>最后，这也未尝不算一种 NTR<br><img data-src="https://image.ponder.work/mweb/2025-04-27---IMG_202504275684_512x682.jpg" alt="IMG_202504275684_512x682"></p><h2 id="安卓环境设置（可选）"><a href="#安卓环境设置（可选）" class="headerlink" title="安卓环境设置（可选）"></a>安卓环境设置（可选）</h2><ol><li>关闭导航栏：系统 &gt; 手势 &gt; 系统导航 &gt; 手势导航</li><li>加快或者关闭系统动画：开发者选项 &gt; 窗口动画缩放、过渡动画缩放、Animator 时长比例 &gt; 关闭或者0.5x</li></ol>]]></content>
    
    
    <summary type="html">&lt;p&gt;有些应用在安卓上是独占的，iOS 上又没有比较好的替代品，而且 iOS 上没有能用安卓模拟器。&lt;br&gt;如果使用多个设备，维护的心智成本又高，被这个问题困扰了许久。&lt;/p&gt;
&lt;p&gt;最近碰巧了解了 &lt;a href=&quot;https://github.com/Genymobile/scrcpy&quot;&gt;scrcpy&lt;/a&gt;, 用于远程控制安卓，终于解决了这个问题。&lt;/p&gt;</summary>
    
    
    
    <category term="工作生活" scheme="https://blog.ponder.work/categories/life/"/>
    
    
    <category term="Android" scheme="https://blog.ponder.work/tags/Android/"/>
    
    <category term="iOS" scheme="https://blog.ponder.work/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>在 VSCode 中用 Rust 刷LeetCode</title>
    <link href="https://blog.ponder.work/2025/03/08/vscode-leetcode-rust/"/>
    <id>https://blog.ponder.work/2025/03/08/vscode-leetcode-rust/</id>
    <published>2025-03-08T20:17:00.000Z</published>
    <updated>2025-03-16T15:18:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍在 VSCode 中配置和使用插件来高效地解决 LeetCode 问题，并使用 Rust 语言编写和测试代码。</p><span id="more"></span><h2 id="vscode-插件"><a href="#vscode-插件" class="headerlink" title="vscode 插件"></a>vscode 插件</h2><ul><li>LeetCode.vscode-leetcode</li><li>pucelle.run-on-save</li><li>rust-lang.rust-analyzer</li></ul><h2 id="项目结构"><a href="#项目结构" class="headerlink" title="项目结构"></a>项目结构</h2><p>cargo new vscode-leetcode-rust</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// tree -I target --dirsfirst</span></span><br><span class="line">.</span><br><span class="line">├── Cargo<span class="selector-class">.lock</span></span><br><span class="line">├── Cargo<span class="selector-class">.toml</span></span><br><span class="line">└── <span class="attribute">src</span></span><br><span class="line">    ├── lib<span class="selector-class">.rs</span></span><br><span class="line">    ├── <span class="selector-tag">main</span><span class="selector-class">.rs</span></span><br><span class="line">    └── solutions</span><br><span class="line">        ├── <span class="number">1</span>_two_sum<span class="selector-class">.rs</span></span><br><span class="line">        ...</span><br></pre></td></tr></table></figure><h2 id="vscode-全局设置"><a href="#vscode-全局设置" class="headerlink" title="vscode 全局设置"></a>vscode 全局设置</h2><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;leetcode.useEndpointTranslation&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span>  <span class="comment">// for english filename</span></span><br><span class="line"><span class="attr">&quot;leetcode.workspaceFolder&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/Users/&lt;your_name&gt;/projects/vscode-leetcode-cn-rust&quot;</span><span class="punctuation">,</span>   </span><br><span class="line"><span class="attr">&quot;leetcode.filePath&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;default&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;folder&quot;</span><span class="punctuation">:</span> <span class="string">&quot;src/solutions&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;filename&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;id&#125;_$&#123;snake_case_name&#125;.$&#123;ext&#125;&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br></pre></td></tr></table></figure><h2 id="用-automod-宏添加新回答到模块"><a href="#用-automod-宏添加新回答到模块" class="headerlink" title="用 automod 宏添加新回答到模块"></a>用 automod 宏添加新回答到模块</h2><p>cargo add automod</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/lib.rs </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> CURRENT: &amp;<span class="type">str</span> = <span class="string">&quot;sdfsdfsd.rs&quot;</span>;  <span class="comment">// trigger rust-analyser recheck</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">mod</span> solutions &#123;</span><br><span class="line">    automod::dir!(<span class="string">&quot;src/solutions&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="触发-rust-analyzer"><a href="#触发-rust-analyzer" class="headerlink" title="触发 rust-analyzer"></a>触发 rust-analyzer</h2><p>用 run-on-save 插件，保存回答时更新 lib.rs，触发 rust-analyzer 重新分析项目，开启新回答的代码补全。</p><p>vscode 项目配置</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;runOnSave.commands&quot;</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">         <span class="comment">// use gnu sed update lib.rs when add solutions (macOS)</span></span><br><span class="line">        <span class="string">&quot;command&quot;</span>: <span class="string">&quot;sh onsave.sh <span class="subst">$&#123;fileBasename&#125;</span>&quot;</span>,  </span><br><span class="line">        <span class="string">&quot;runIn&quot;</span>: <span class="string">&quot;backend&quot;</span>,</span><br><span class="line">        <span class="string">&quot;finishStatusMessage&quot;</span>: <span class="string">&quot;touched <span class="subst">$&#123;workspaceFolderBasename&#125;</span>&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p><code>onsave.sh</code>脚本，macOS使用 gnused，linux 使用默认 sed 就好。<br>通过切换模块是否为pub来触发 rust-analyzer 识别新回答</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">FILE=<span class="variable">$1</span></span><br><span class="line"><span class="keyword">if</span> [[ `grep <span class="string">&quot;<span class="variable">$FILE</span>&quot;</span> src/lib.rs | <span class="built_in">wc</span> -l` -eq 0 ]]; <span class="keyword">then</span></span><br><span class="line">    gsed -i -E <span class="string">&quot;/CURRENT/c\const CURRENT: &amp;str = \&quot;<span class="variable">$FILE</span>\&quot;;&quot;</span> src/lib.rs</span><br><span class="line">    <span class="keyword">if</span> [[ `grep <span class="string">&#x27;::dir!(pub&#x27;</span> src/lib.rs | <span class="built_in">wc</span> -l` -eq 1 ]]; <span class="keyword">then</span></span><br><span class="line">        gsed -i <span class="string">&quot;s|::dir!(pub |::dir!(|&quot;</span> src/lib.rs</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        gsed -i <span class="string">&quot;s|::dir!(|::dir!(pub |&quot;</span> src/lib.rs</span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="编写本地测试用例"><a href="#编写本地测试用例" class="headerlink" title="编写本地测试用例"></a>编写本地测试用例</h2><p>把测试代码写在 <code>&quot;// @lc code=end&quot;</code> 后面，需要定义 Solution 结构体，可能还需要定义参数的结构体。</p><figure class="highlight elixir"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// <span class="variable">@lc</span> code=<span class="keyword">end</span></span><br><span class="line">struct <span class="title class_">Solution</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">#[test]</span></span><br><span class="line"><span class="keyword">fn</span> test_a() &#123;</span><br><span class="line">    // let res = <span class="title class_">Solution</span>::is_valid(<span class="title class_">String</span>::from(<span class="string">&quot;()[]&#123;&#125;&quot;</span>));</span><br><span class="line">    // println!(<span class="string">&quot;RESUTL\t&#123;:?&#125;&quot;</span>, res);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img data-src="https://image.ponder.work/mweb/2025-03-16---17420958075794.jpg" alt="-w340"></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍在 VSCode 中配置和使用插件来高效地解决 LeetCode 问题，并使用 Rust 语言编写和测试代码。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Rust" scheme="https://blog.ponder.work/tags/Rust/"/>
    
  </entry>
  
  <entry>
    <title>跨域的那些事</title>
    <link href="https://blog.ponder.work/2023/08/15/cross-domain-things/"/>
    <id>https://blog.ponder.work/2023/08/15/cross-domain-things/</id>
    <published>2023-08-15T15:00:00.000Z</published>
    <updated>2024-07-12T09:22:00.000Z</updated>
    
    <content type="html"><![CDATA[<ul><li>什么是跨域？<ul><li>就是当前域访问了非本域的资源。对于http来说，url代表资源，也就是访问了非本域的url。</li></ul></li></ul><span id="more"></span><ul><li>域的定义是啥？<ul><li>这里的域，也就是同源策略（Same-origin policy）中的源。</li><li>如果两个 URL 的协议、端口和主机都相同的话，则这两个 URL 是同源的。</li><li>当前域：当前网页的域；目标域：访问的资源所在的域</li></ul></li><li>为什么要限制跨域请求？信息泄露<ul><li>js直接读取浏览器的目标域的sessionid、cookie，伪造请求。<ul><li>比如，当前站点是恶意站点，它用js请求银行站点，盗取信息。</li></ul></li><li>CSRF，如img标签的src会被访问，利用受害者已认证的身份，在不知情的情况下向受害者认证的站点发起恶意请求。<ul><li>通过在src里构造url，恶意请求目标域<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;img <span class="attribute">src</span>=<span class="string">&quot;https://bank.com/transfer?amount=1000&amp;to=attackerAccount&quot;</span> </span><br><span class="line">      <span class="attribute">style</span>=<span class="string">&quot;display:none;&quot;</span>&gt;</span><br></pre></td></tr></table></figure></li></ul></li><li>XSS跨站脚本(Cross-site scripting)注入，导致用户在信息泄露<ul><li>一般是接受了用户构造的输入，输入里包含恶意脚本内容，内容未被转义，又输出在页面上，从而被执行</li><li>其他用户查看了该页面，注入的脚本被执行</li></ul></li></ul></li><li>怎么限制跨域？浏览器的同源策略<ul><li>禁止的<ul><li>DOM 同源策略： 不同源的dom之间不能相互操作，多个iframe的情况</li><li>XMLHttpRequest 同源策略： 禁止请求不同源url</li><li>Cookie、LocalStorage、IndexedDB 等存储性内容同源策略</li></ul></li><li>允许的<ul><li>页面中的链接，重定向以及表单提交</li><li><code>&lt;script&gt;、&lt;img&gt;、&lt;link&gt;</code>这些包含 src 属性的标签可以加载跨域资源。（只能GET）</li></ul></li></ul></li><li>限制跨域导致哪些不便？<ul><li>前后端分离开发时，localhost不能正常访问后端资源</li><li>一些公共的api不能被访问</li><li>https的页面的http的静态资源，不能加载</li></ul></li><li>如何绕过同源策略？<ul><li>浏览器启动参数（在用户端操作）</li><li>反向代理（在当前域操作）</li><li>JSONP（在目标域操作）<ul><li>利用<code>&lt;script&gt;</code>允许跨域的特点，设置标签的src为目标域，动态生成需要的javascript内容</li></ul></li><li>跨源资源共享（CORS）（目标域操作）<ul><li>设置相应的reponse header<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Access</span>-Control-Allow-Origin: https://foo.example  // 所允许的来源域</span><br><span class="line"><span class="keyword">Access</span>-Control-Allow-Methods: POST, <span class="keyword">GET</span>, <span class="keyword">OPTIONS</span>   // 所允许的请求方法</span><br><span class="line"><span class="keyword">Access</span>-Control-Allow-Headers: X-PINGOTHER, Content-<span class="keyword">Type</span>  // 所允许的请求<span class="keyword">header</span></span><br><span class="line"><span class="keyword">Access</span>-Control-Max-Age: <span class="number">86400</span></span><br></pre></td></tr></table></figure></li></ul></li></ul></li><li>参考<ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy">https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Glossary/CSRF">https://developer.mozilla.org/zh-CN/docs/Glossary/CSRF</a></li><li><a href="https://juejin.cn/post/6879360544323665928">https://juejin.cn/post/6879360544323665928</a></li><li><a href="https://juejin.cn/post/6867096987804794888">https://juejin.cn/post/6867096987804794888</a></li></ul></li></ul>]]></content>
    
    
    <summary type="html">&lt;ul&gt;
&lt;li&gt;什么是跨域？&lt;ul&gt;
&lt;li&gt;就是当前域访问了非本域的资源。对于http来说，url代表资源，也就是访问了非本域的url。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Web" scheme="https://blog.ponder.work/tags/Web/"/>
    
  </entry>
  
  <entry>
    <title>HomeBrew 与无 root 权限 Linux 环境包管理</title>
    <link href="https://blog.ponder.work/2023/05/28/homebrew-as-non-root-package-manager/"/>
    <id>https://blog.ponder.work/2023/05/28/homebrew-as-non-root-package-manager/</id>
    <published>2023-05-28T20:00:00.000Z</published>
    <updated>2023-05-28T23:49:17.000Z</updated>
    
    <content type="html"><![CDATA[<p>一些公用的 Linux 服务器，处于维护以及安全考虑，一般只会提供普通权限用户给使用者。<br>普通用户的权限满足日常使用是够了，但是难以配置自己的开发环境，安装一些自己需要的包。</p><p>如果都从源码编译安装软件，依赖的维护过于复杂，初始编译工具链的版本可能也不满足需求，如 gcc 版本过低。<br>如果申请 sudo 权限或者请求更新系统或安装 docker，后期责任难以界定，运维和管理员一般也不会同意。</p><p>所以，最优方案还是有需求的用户在个人目录维护自己的工具链和环境。下文方案为围绕 HomeBrew 构建。</p><span id="more"></span><h2 id="安装-miniconda-解决前置依赖"><a href="#安装-miniconda-解决前置依赖" class="headerlink" title="安装 miniconda 解决前置依赖"></a>安装 miniconda 解决前置依赖</h2><p>如果你的系统比较新，可以直接尝试<code>安装 HomeBrew</code>。</p><p>基于上面讨论的内容，公用服务器一般存在系统版本低的问题，是 centos7 或者 centos6 也毫不稀奇，而且如 glibc 等库的版本也非常低。</p><p>安装 HomeBrew 有两个强依赖，git 及 curl，而且依赖的版本都比较高，centos7 的版本也不能满足。<br>另外，由于 Brew 不少软件都需要从源码编译，gcc 和良好的网络环境也不可缺少。</p><p>幸好 miniconda 能够解决以上几点问题。miniconda 只是提供 HomeBrew 安装的依赖，后续可以删除。</p><p>配置 conda 源（可选）： 新建 <code>.condarc</code>，包含以下内容</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">channels:</span><br><span class="line">  - defaults</span><br><span class="line">show_channel_urls: true</span><br><span class="line">default_channels:</span><br><span class="line">  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main</span><br><span class="line">  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/<span class="attribute">r</span></span><br><span class="line"><span class="attribute">  - https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2</span></span><br><span class="line"><span class="attribute">custom_channels</span>:</span><br><span class="line">  <span class="attribute">conda-forge</span>: <span class="attribute">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/cloud</span></span><br><span class="line">  <span class="attribute">msys2</span>: <span class="attribute">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/cloud</span></span><br><span class="line">  <span class="attribute">bioconda</span>: <span class="attribute">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/cloud</span></span><br><span class="line">  <span class="attribute">menpo</span>: <span class="attribute">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/cloud</span></span><br><span class="line">  <span class="attribute">pytorch</span>: <span class="attribute">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/cloud</span></span><br><span class="line">  <span class="attribute">pytorch-lts</span>: <span class="attribute">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/cloud</span></span><br><span class="line">  <span class="attribute">simpleitk</span>: <span class="attribute">https</span>:<span class="comment">//mirrors.tuna.tsinghua.edu.cn/anaconda/cloud</span></span><br></pre></td></tr></table></figure><p>下载及安装 miniconda</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">如果服务器的内置证书已过期， 增加 --no-check-certificate 条件跳过证书验证</span></span><br><span class="line">wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh &amp;&amp; chmod +x Miniconda3-latest-Linux-x86_64.sh</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装</span></span><br><span class="line">./Miniconda3-latest-Linux-x86_64.sh -b -p ~/miniconda3</span><br><span class="line">source ~/miniconda3/etc/profile.d/conda.sh</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装所需包</span></span><br><span class="line">conda install -y gcc_linux-64 gxx_linux-64 curl git</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">链接 gcc，等 HomeBrew 安装完成这些链接可以删掉</span></span><br><span class="line">cd ~/miniconda3/bin/</span><br><span class="line">ln -s x86_64-conda_cos6-linux-gnu-gcc gcc</span><br><span class="line">ln -s x86_64-conda_cos6-linux-gnu-cpp c++</span><br><span class="line">ln -s gcc cc</span><br></pre></td></tr></table></figure><h2 id="配置安装环境变量"><a href="#配置安装环境变量" class="headerlink" title="配置安装环境变量"></a>配置安装环境变量</h2><p>这些环境变量也可以配置到 bashrc 等文件，使之永久生效</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置 curl 和 git，可选</span></span><br><span class="line">export HOMEBREW_CURL_PATH=~/miniconda3/bin/curl</span><br><span class="line">export HOMEBREW_GIT_PATH=~/miniconda3/bin/git</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置安装源为清华源，如果网络畅通可忽略，清华源也可能403</span></span><br><span class="line">export HOMEBREW_INSTALL_FROM_API=1</span><br><span class="line">export HOMEBREW_API_DOMAIN=&quot;https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api&quot;</span><br><span class="line">export HOMEBREW_BOTTLE_DOMAIN=&quot;https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles&quot;</span><br><span class="line">export HOMEBREW_BREW_GIT_REMOTE=&quot;https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git&quot;</span><br><span class="line">export HOMEBREW_CORE_GIT_REMOTE=&quot;https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git&quot;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="安装-HomeBrew"><a href="#安装-HomeBrew" class="headerlink" title="安装 HomeBrew"></a>安装 HomeBrew</h2><p>由于没有 root 权限，HomeBrew 需要手动安装。<br>由于是手动安装，位置与默认安装位置不同，很多预编译的包就不能用了，都得从源码编译，所以网络和机器性能以及耐心很重要。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载，也可使用清华源 https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git</span></span><br><span class="line">git clone https://github.com/Homebrew/brew ~/.homebrew</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装</span></span><br><span class="line">eval &quot;$(~/.homebrew/bin/brew shellenv)&quot;</span><br><span class="line">brew update --force --quiet</span><br><span class="line">chmod -R go-w &quot;$(brew --prefix)/share/zsh&quot;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">自动加载 brew</span></span><br><span class="line">echo &#x27;eval &quot;$(~/.homebrew/bin/brew shellenv)&quot;&#x27; &gt;&gt; ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://docs.brew.sh/Installation">https://docs.brew.sh/Installation</a></li><li><a href="https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/">https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/</a></li><li><a href="https://mirrors.tuna.tsinghua.edu.cn/help/homebrew/">https://mirrors.tuna.tsinghua.edu.cn/help/homebrew/</a></li><li><a href="https://github.com/tuna/issues/issues/1353">https://github.com/tuna/issues/issues/1353</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;一些公用的 Linux 服务器，处于维护以及安全考虑，一般只会提供普通权限用户给使用者。&lt;br&gt;普通用户的权限满足日常使用是够了，但是难以配置自己的开发环境，安装一些自己需要的包。&lt;/p&gt;
&lt;p&gt;如果都从源码编译安装软件，依赖的维护过于复杂，初始编译工具链的版本可能也不满足需求，如 gcc 版本过低。&lt;br&gt;如果申请 sudo 权限或者请求更新系统或安装 docker，后期责任难以界定，运维和管理员一般也不会同意。&lt;/p&gt;
&lt;p&gt;所以，最优方案还是有需求的用户在个人目录维护自己的工具链和环境。下文方案为围绕 HomeBrew 构建。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Linux" scheme="https://blog.ponder.work/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>给 macOS 词典增加生词本功能</title>
    <link href="https://blog.ponder.work/2022/11/12/add-wordlist-for-macOS-dictionary/"/>
    <id>https://blog.ponder.work/2022/11/12/add-wordlist-for-macOS-dictionary/</id>
    <published>2022-11-12T20:00:00.000Z</published>
    <updated>2022-11-04T19:40:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>macOS 系统的自带词典应用非常强大，与其他应用整合很好，快捷取词很方便（command+control+d）。<br>但是美中不足的是缺少生词本功能，查了单词又很容易忘记，对语言学习者来说就有些不便了。</p><p>经过本强迫症的探索，终于找到基于 Karabiner-Elements + Automator + Logseq 的完美生词本方案。<br>最后的效果是，快捷键取词的同时记录单词卡片到Logseq对应的笔记。</p><span id="more"></span><h2 id="词典词库扩充"><a href="#词典词库扩充" class="headerlink" title="词典词库扩充"></a>词典词库扩充</h2><p>参考<a href="https://zhuanlan.zhihu.com/p/433646737">知乎文章</a>安装好《朗道英汉字典5.0》<br>这是为了有个释义简洁的词典，方便后续生成生词本词条</p><p><img data-src="https://image.ponder.work/mweb/2022-11-02-16673997943438.jpg"></p><h2 id="编写workflow"><a href="#编写workflow" class="headerlink" title="编写workflow"></a>编写workflow</h2><p>使用 macOS 自带应用 Automator（自动操作）编写workflow，将当前鼠标所在位置的文本提取并保存制卡。</p><p>首先打开 Automator.app 新建一个 Quick Aciont(快速操作)<br><img data-src="https://image.ponder.work/mweb/2022-11-02-16673999123628.jpg"><br>然后依次拖入“获得词语定义”，“运行Shell脚本”等步骤，并调整如下几个位置的选项。<br><img data-src="https://image.ponder.work/mweb/2022-11-02-16674004811769.jpg"></p><p>修改脚本里的代码为如下内容，生词本路径相应替换，并相应位置新建好生词本文件。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding:utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> unicode_literals, print_function</span><br><span class="line"><span class="keyword">import</span> sys, os, io, subprocess</span><br><span class="line"></span><br><span class="line">FILE=os.path.expanduser(<span class="string">&quot;~/weiyun_sync/!sync/logseq-note/pages/生词本.md&quot;</span>)</span><br><span class="line">output = []</span><br><span class="line">text = sys.argv[<span class="number">1</span>].decode(<span class="string">&#x27;utf8&#x27;</span>) <span class="keyword">if</span> sys.version_info.major == <span class="number">2</span> <span class="keyword">else</span> sys.argv[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line">lines = [i.strip() <span class="keyword">for</span> i <span class="keyword">in</span> text.splitlines() <span class="keyword">if</span> i.strip()]</span><br><span class="line"><span class="keyword">if</span> <span class="built_in">len</span>(lines) &lt; <span class="number">2</span>:</span><br><span class="line">    exit(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">word = lines[<span class="number">0</span>]</span><br><span class="line"><span class="keyword">if</span> lines[<span class="number">1</span>][<span class="number">0</span>] == <span class="string">&#x27;*&#x27;</span>:</span><br><span class="line">    output.append(<span class="string">&#x27;- &#123;&#125;\t&#123;&#125;  [[card]]&#x27;</span>.<span class="built_in">format</span>(word, lines[<span class="number">1</span>]))</span><br><span class="line">    lines = lines[<span class="number">2</span>:]</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    output.append(<span class="string">&#x27;- &#123;&#125;\t  [[card]]&#x27;</span>.<span class="built_in">format</span>(word))</span><br><span class="line">    lines = lines[<span class="number">1</span>:]</span><br><span class="line">output.append(<span class="string">&#x27;\t- &#123;&#125;&#x27;</span>.<span class="built_in">format</span>(lines[<span class="number">0</span>]))</span><br><span class="line"><span class="keyword">for</span> line <span class="keyword">in</span> lines[<span class="number">1</span>:]:</span><br><span class="line">    output.append(<span class="string">&#x27;\t  &#x27;</span> + line)</span><br><span class="line"></span><br><span class="line">old_words = <span class="built_in">set</span>()</span><br><span class="line"><span class="keyword">with</span> io.<span class="built_in">open</span>(FILE, <span class="string">&#x27;r&#x27;</span>, encoding=<span class="string">&#x27;utf8&#x27;</span>) <span class="keyword">as</span> fp:</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> fp:</span><br><span class="line">        parts = line.split()</span><br><span class="line">        <span class="keyword">if</span> line.startswith(<span class="string">&#x27;-&#x27;</span>) <span class="keyword">and</span> <span class="built_in">len</span>(parts) &gt; <span class="number">1</span>:</span><br><span class="line">            old_words.add(parts[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> word <span class="keyword">not</span> <span class="keyword">in</span> old_words:</span><br><span class="line">    <span class="keyword">with</span> io.<span class="built_in">open</span>(FILE, <span class="string">&#x27;a&#x27;</span>, encoding=<span class="string">&#x27;utf8&#x27;</span>) <span class="keyword">as</span> fp:</span><br><span class="line">        fp.write(<span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line">        fp.write(<span class="string">&#x27;\n&#x27;</span>.join(output))</span><br><span class="line">        fp.write(<span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line">    subprocess.check_call([<span class="string">&#x27;osascript&#x27;</span>, <span class="string">&#x27;-e&#x27;</span>, <span class="string">u&#x27;display notification &quot;添加 &#123;&#125;&quot; with title &quot;生词本&quot;&#x27;</span>.<span class="built_in">format</span>(word)])</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    subprocess.check_call([<span class="string">&#x27;osascript&#x27;</span>, <span class="string">&#x27;-e&#x27;</span>, <span class="string">u&#x27;display notification &quot;跳过 &#123;&#125;&quot; with title &quot;生词本&quot;&#x27;</span>.<span class="built_in">format</span>(word)])</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>选择路径保存好 workflow，然后在 <code>键盘 - 快捷键 - 服务</code> 中能看到新建的workflow。<br>为它设置快捷键 <code>command + shift + alt + 1</code><br><img data-src="https://image.ponder.work/mweb/2022-11-02-16674009451584.jpg"></p><h2 id="Karabiner-Elements"><a href="#Karabiner-Elements" class="headerlink" title="Karabiner-Elements"></a>Karabiner-Elements</h2><p><a href="https://karabiner-elements.pqrs.org/">Karabiner-Elements</a> 是 macOS 平台的一个重新映射快捷键的软件。<br>这里我们使用它将“查询单词”和“触发workflow”整合在一起，当然它还支持很多用途，这里就不赘述了。</p><p>注意确保Karabiner相关权限，并且设置中下图相关设备是勾选状态</p><p><img data-src="https://image.ponder.work/mweb/2022-11-04-16675619005392.jpg"></p><p>安装好Karabiner-Elements后，打开它的配置文件<br>路径在 <code>/Users/&lt;用户名&gt;/.config/karabiner/karabiner.json</code></p><p>在 <code>profiles -&gt; complex_modifications -&gt; rules</code> 列表中增加一项配置，内容如下。<br>然后保存，Karabiner会自动加载新的配置。</p><p>这里是将鼠标的侧键（靠前的）映射为查单词的快捷键，实现一键查词。<br>也可以根据需要更改按键，通过<a href="https://karabiner-elements.pqrs.org/docs/manual/operation/eventviewer/">EventViewer</a>可以查看按键代码，配置文件格式可参考<a href="https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/to/">官方文档</a></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Mouse&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;manipulators&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;from&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;pointing_button&quot;</span><span class="punctuation">:</span> <span class="string">&quot;button5&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;to&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;pointing_button&quot;</span><span class="punctuation">:</span> <span class="string">&quot;button1&quot;</span></span><br><span class="line">                <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;pointing_button&quot;</span><span class="punctuation">:</span> <span class="string">&quot;button1&quot;</span></span><br><span class="line">                <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;key_code&quot;</span><span class="punctuation">:</span> <span class="string">&quot;d&quot;</span><span class="punctuation">,</span></span><br><span class="line">                    <span class="attr">&quot;modifiers&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                        <span class="string">&quot;left_command&quot;</span><span class="punctuation">,</span></span><br><span class="line">                        <span class="string">&quot;left_control&quot;</span></span><br><span class="line">                    <span class="punctuation">]</span></span><br><span class="line">                <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;key_code&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">                    <span class="attr">&quot;modifiers&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                        <span class="string">&quot;left_option&quot;</span><span class="punctuation">,</span></span><br><span class="line">                        <span class="string">&quot;left_shift&quot;</span><span class="punctuation">,</span></span><br><span class="line">                        <span class="string">&quot;left_command&quot;</span></span><br><span class="line">                    <span class="punctuation">]</span></span><br><span class="line">                <span class="punctuation">&#125;</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;basic&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><img data-src="https://image.ponder.work/mweb/2022-11-02-16674017788245.jpg"></p><h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><p>可以看到 Logseq 中卡片生成的效果<br><img data-src="https://image.ponder.work/mweb/2022-11-02-16674007610777.jpg"><br><img data-src="https://image.ponder.work/mweb/2022-11-02-16674023982597.jpg"></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://karabiner-elements.pqrs.org/docs/">https://karabiner-elements.pqrs.org/docs/</a></li><li><a href="https://zhuanlan.zhihu.com/p/433646737">https://zhuanlan.zhihu.com/p/433646737</a></li><li><a href="https://hectorguo.com/zh/save-words-in-dictionary/">https://hectorguo.com/zh/save-words-in-dictionary/</a></li><li><a href="https://github.com/jjgod/mac-dictionary-kit">https://github.com/jjgod/mac-dictionary-kit</a></li><li><a href="https://lightcss.com/mac-dictionary/">https://lightcss.com/mac-dictionary/</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;macOS 系统的自带词典应用非常强大，与其他应用整合很好，快捷取词很方便（command+control+d）。&lt;br&gt;但是美中不足的是缺少生词本功能，查了单词又很容易忘记，对语言学习者来说就有些不便了。&lt;/p&gt;
&lt;p&gt;经过本强迫症的探索，终于找到基于 Karabiner-Elements + Automator + Logseq 的完美生词本方案。&lt;br&gt;最后的效果是，快捷键取词的同时记录单词卡片到Logseq对应的笔记。&lt;/p&gt;</summary>
    
    
    
    <category term="工作生活" scheme="https://blog.ponder.work/categories/life/"/>
    
    
    <category term="mac" scheme="https://blog.ponder.work/tags/mac/"/>
    
  </entry>
  
  <entry>
    <title>关闭子进程打开的文件描述符</title>
    <link href="https://blog.ponder.work/2022/08/30/close-subprocess-opened-fd/"/>
    <id>https://blog.ponder.work/2022/08/30/close-subprocess-opened-fd/</id>
    <published>2022-08-30T22:48:00.000Z</published>
    <updated>2022-08-31T15:32:47.000Z</updated>
    
    <content type="html"><![CDATA[<p>我们在测试代码时，由于需要经常重启服务，经常会发现服务端口被占用。<br>一般kill掉后台进程就ok了，但是如果服务有启动一些常驻的后台程序，可能也会导致端口不能释放。</p><p>在类UNIX系统中，一切被打开的文件、端口被抽象为文件描述符（file descriptor）<br>从python3.4开始，文件描述符默认是non-inheritable，也就是子进程不会共享文件描述符。</p><span id="more"></span><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>一般为了实现多进程、多线程的webserver，服务端口fd必须设置为继承（set_inheritable），这样才能多进程监听一个端口（配合SO_REUSEPORT）<br>典型的是使用flask的测试服务器的场景，这里我们写一段代码模拟。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> socket, os</span><br><span class="line">server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">server.bind((<span class="string">&#x27;127.0.0.1&#x27;</span>, <span class="number">22222</span>))</span><br><span class="line">server.set_inheritable(<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">os.system(<span class="string">&quot;python -c &#x27;import time;time.sleep(1000)&#x27; &quot;</span>)</span><br></pre></td></tr></table></figure><p>我们通过<code>lsof -p {pid}</code>可以看到这两个进程的所有文件描述符<br>server进程, 可以看到服务端口的fd是4</p><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">COMMAND   PID  FD      TYPE             DEVICE  SIZE/OFF       NODE NAME</span><br><span class="line">ptpython<span class="number"> 6214 </span>cwd       DIR              253,0     <span class="number"> 4096 </span><span class="number"> 872946898 </span>/</span><br><span class="line">...</span><br><span class="line">ptpython<span class="number"> 6214 </span>  0u      CHR             136,13       0t0        <span class="number"> 16 </span>/dev/pts/13</span><br><span class="line">ptpython<span class="number"> 6214 </span>  1u      CHR             136,13       0t0        <span class="number"> 16 </span>/dev/pts/13</span><br><span class="line">ptpython<span class="number"> 6214 </span>  2u      CHR             136,13       0t0        <span class="number"> 16 </span>/dev/pts/13</span><br><span class="line">ptpython<span class="number"> 6214 </span>  3r      CHR                1,9       0t0      <span class="number"> 2057 </span>/dev/urandom</span><br><span class="line">ptpython<span class="number"> 6214 </span>  4u     sock                0,7       0t0  <span class="number"> 58345077 </span>protocol: TCP</span><br><span class="line">ptpython<span class="number"> 6214 </span>  5u  a_inode               0,10        <span class="number"> 0 </span>     <span class="number"> 8627 </span>[eventpoll]</span><br><span class="line">ptpython<span class="number"> 6214 </span>  6u     unix 0x0000000000000000       0t0  <span class="number"> 58368029 </span>socket</span><br><span class="line">ptpython<span class="number"> 6214 </span>  7u     unix 0x0000000000000000       0t0  <span class="number"> 58368030 </span>socket</span><br></pre></td></tr></table></figure><p>sleep子进程，也拥有fd&#x3D;4的文件描述符</p><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">COMMAND   PID  FD   TYPE DEVICE  SIZE/OFF       NODE NAME</span><br><span class="line">python <span class="number"> 18022 </span>cwd    DIR  253,0     <span class="number"> 4096 </span><span class="number"> 872946898 </span>/</span><br><span class="line">...</span><br><span class="line">python <span class="number"> 18022 </span>  0u   CHR 136,13       0t0        <span class="number"> 16 </span>/dev/pts/13</span><br><span class="line">python <span class="number"> 18022 </span>  1u   CHR 136,13       0t0        <span class="number"> 16 </span>/dev/pts/13</span><br><span class="line">python <span class="number"> 18022 </span>  2u   CHR 136,13       0t0        <span class="number"> 16 </span>/dev/pts/13</span><br><span class="line">python <span class="number"> 18022 </span>  4u  sock    0,7       0t0  <span class="number"> 58345077 </span>protocol: TCP</span><br></pre></td></tr></table></figure><p>如果server进程退出时，sleep进程没有退出，fd&#x3D;4对应的端口就被占用了，服务也就不能正常启动了。</p><h2 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h2><h3 id="手动清理"><a href="#手动清理" class="headerlink" title="手动清理"></a>手动清理</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">os.system(<span class="string">f&#x27;lsof -p <span class="subst">&#123;os.getpid()&#125;</span>&#x27;</span>)</span><br><span class="line">os.closerange(<span class="number">3</span>, <span class="number">100</span>)  <span class="comment"># 这里假定打开文件描述符不会超过100</span></span><br><span class="line">time.sleep(<span class="number">5</span>)</span><br><span class="line">os.system(<span class="string">f&#x27;lsof -p <span class="subst">&#123;os.getpid()&#125;</span>&#x27;</span>)</span><br><span class="line"><span class="comment"># 后面执行需要的业务代码</span></span><br></pre></td></tr></table></figure><h3 id="使用close-fds"><a href="#使用close-fds" class="headerlink" title="使用close_fds"></a>使用close_fds</h3><p>使用subprocess库而不是os来启动子程序， 通过close_fds参数关闭多余的文件描述符</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line">subprocess.call(<span class="string">&quot;python -c &#x27;import time;time.sleep(1000)&#x27;&quot;</span>, shell=<span class="literal">True</span>, close_fds=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://docs.python.org/3/library/os.html#inheritance-of-file-descriptors">https://docs.python.org/3/library/os.html#inheritance-of-file-descriptors</a></li><li><a href="https://docs.python.org/3/library/subprocess.html#subprocess.Popen">https://docs.python.org/3/library/subprocess.html#subprocess.Popen</a></li><li><a href="https://stackoverflow.com/questions/2023608/check-what-files-are-open-in-python#answer-25069136">https://stackoverflow.com/questions/2023608/check-what-files-are-open-in-python#answer-25069136</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;我们在测试代码时，由于需要经常重启服务，经常会发现服务端口被占用。&lt;br&gt;一般kill掉后台进程就ok了，但是如果服务有启动一些常驻的后台程序，可能也会导致端口不能释放。&lt;/p&gt;
&lt;p&gt;在类UNIX系统中，一切被打开的文件、端口被抽象为文件描述符（file descriptor）&lt;br&gt;从python3.4开始，文件描述符默认是non-inheritable，也就是子进程不会共享文件描述符。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Python" scheme="https://blog.ponder.work/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>容器内进程优雅退出</title>
    <link href="https://blog.ponder.work/2022/07/10/gracefully-shutdown-container/"/>
    <id>https://blog.ponder.work/2022/07/10/gracefully-shutdown-container/</id>
    <published>2022-07-10T21:48:00.000Z</published>
    <updated>2022-07-11T15:34:10.000Z</updated>
    
    <content type="html"><![CDATA[<p>在使用 docker 时，常常会碰到进程退出时资源清理的问题，比如保证当前请求处理完成，再退出程序。</p><p>当执行 <code>docker stop xxx</code> 时，docker会向主进程（pid&#x3D;1）发送 <code>SIGTERM</code> 信号<br>如果在一定时间(默认为10s)内进程没有退出，会进一步发送 <code>SIGKILL</code> 直接杀死程序，该信号既不能被捕捉也不能被忽略。</p><p>一般的web框架或者rpc框架都集成了 <code>SIGTERM</code> 信号处理程序， 一般不用担心优雅退出的问题。<br>但是如果你的容器内有多个程序（称为胖容器，一般不推荐），那么就需要做一些操作保证所有程序优雅退出。</p><span id="more"></span><h2 id="signals"><a href="#signals" class="headerlink" title="signals"></a>signals</h2><p>信号是一种进程间通信机制，它给应用程序提供一种异步的软件中断，使应用程序有机会接受其他程序活终端发送的命令(即信号)。</p><p>应用程序收到信号后，有三种处理方式：忽略，默认，或捕捉。</p><p>常见信号：</p><table><thead><tr><th>信号名称</th><th>信号数</th><th>描述</th><th align="left">默认操作</th></tr></thead><tbody><tr><td>SIGHUP</td><td>1</td><td>当用户退出Linux登录时，前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。对于与终端脱离关系的守护进程，这个信号用于通知它重新读取配置文件。</td><td align="left">终止进程</td></tr><tr><td>SIGINT</td><td>2</td><td>程序终止(interrupt)信号，在用户键入 Ctrl+C 时发出。</td><td align="left">终止进程</td></tr><tr><td>SIGQUIT</td><td>3</td><td>和SIGINT类似，但由QUIT字符(通常是Ctrl &#x2F;)来控制。</td><td align="left">终止进程并dump core</td></tr><tr><td>SIGFPE</td><td>8</td><td>在发生致命的算术运算错误时发出。不仅包括浮点运算错误，还包括溢出及除数为0等其它所有的算术错误。</td><td align="left">终止进程并dump core</td></tr><tr><td>SIGKILL</td><td>9</td><td>用来立即结束程序的运行。本信号不能被阻塞，处理和忽略。</td><td align="left">终止进程</td></tr><tr><td>SIGALRM</td><td>14</td><td>时钟定时信号，计算的是实际的时间或时钟时间。alarm 函数使用该信号。</td><td align="left">终止进程</td></tr><tr><td>SIGTERM</td><td>15</td><td>通常用来要求程序自己正常退出；kill 命令缺省产生这个信号。</td><td align="left">终止进程</td></tr></tbody></table><h2 id="Dockerfile"><a href="#Dockerfile" class="headerlink" title="Dockerfile"></a>Dockerfile</h2><p>下面以 supervisor 为例，Dockerfile 如下</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> centos:centos7</span><br><span class="line"><span class="keyword">ENV</span> PYTHONUNBUFFERED=<span class="number">1</span> TZ=Asia/Shanghai</span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> yum -y install epel-release &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    yum -y install supervisor &amp;&amp; \</span></span><br><span class="line"><span class="language-bash">    yum -y clean all  &amp;&amp; <span class="built_in">rm</span> -rf /var/cache</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> ./ /root/</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [ <span class="string">&quot;/usr/bin/supervisord&quot;</span>, <span class="string">&quot;-n&quot;</span>, <span class="string">&quot;-c&quot;</span>, <span class="string">&quot;/etc/supervisord.conf&quot;</span> ]</span></span><br></pre></td></tr></table></figure><h2 id="trap"><a href="#trap" class="headerlink" title="trap"></a>trap</h2><p>正常情况，容器退出时supervisor启动的其他程序并不会收到 <code>SIGTERM</code> 信号，导致子程序直接退出了。</p><p>这里使用 <code>trap</code> 对程序的异常处理进行包装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">trap</span> &lt;siginal handler&gt; &lt;signal 1&gt; &lt;signal 2&gt; ...</span><br></pre></td></tr></table></figure><p>新建一个初始化脚本，<code>init.sh</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"></span><br><span class="line">/usr/bin/supervisord -n -c /etc/supervisord.conf &amp;</span><br><span class="line"></span><br><span class="line"><span class="built_in">trap</span> <span class="string">&quot;supervisorctl stop all &amp;&amp; sleep 3&quot;</span> TERM INT</span><br><span class="line"></span><br><span class="line"><span class="built_in">wait</span></span><br></pre></td></tr></table></figure><p>修改 ENTRYPOINT 为如下</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ENTRYPOINT <span class="selector-attr">[<span class="string">&quot;sh&quot;</span>, <span class="string">&quot;/root/init.sh&quot;</span>]</span></span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.ctl.io/developers/blog/post/gracefully-stopping-docker-containers/">https://www.ctl.io/developers/blog/post/gracefully-stopping-docker-containers/</a></li><li><a href="https://www.cnblogs.com/taobataoma/archive/2007/08/30/875743.html">https://www.cnblogs.com/taobataoma/archive/2007/08/30/875743.html</a></li><li><a href="https://wangchujiang.com/linux-command/c/trap.html">https://wangchujiang.com/linux-command/c/trap.html</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;在使用 docker 时，常常会碰到进程退出时资源清理的问题，比如保证当前请求处理完成，再退出程序。&lt;/p&gt;
&lt;p&gt;当执行 &lt;code&gt;docker stop xxx&lt;/code&gt; 时，docker会向主进程（pid&amp;#x3D;1）发送 &lt;code&gt;SIGTERM&lt;/code&gt; 信号&lt;br&gt;如果在一定时间(默认为10s)内进程没有退出，会进一步发送 &lt;code&gt;SIGKILL&lt;/code&gt; 直接杀死程序，该信号既不能被捕捉也不能被忽略。&lt;/p&gt;
&lt;p&gt;一般的web框架或者rpc框架都集成了 &lt;code&gt;SIGTERM&lt;/code&gt; 信号处理程序， 一般不用担心优雅退出的问题。&lt;br&gt;但是如果你的容器内有多个程序（称为胖容器，一般不推荐），那么就需要做一些操作保证所有程序优雅退出。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Linux" scheme="https://blog.ponder.work/tags/Linux/"/>
    
    <category term="Docker" scheme="https://blog.ponder.work/tags/Docker/"/>
    
  </entry>
  
  <entry>
    <title>Python 循环变量泄露与延迟绑定</title>
    <link href="https://blog.ponder.work/2022/03/04/python-loop-variables-leak/"/>
    <id>https://blog.ponder.work/2022/03/04/python-loop-variables-leak/</id>
    <published>2022-03-04T20:17:00.000Z</published>
    <updated>2022-03-05T13:15:04.000Z</updated>
    
    <content type="html"><![CDATA[<p>循环变量泄露与延迟绑定叠加在一起，会产生一些让人迷惑的结果。</p><span id="more"></span><h2 id="梦开始的地方"><a href="#梦开始的地方" class="headerlink" title="梦开始的地方"></a>梦开始的地方</h2><p>先看看一开始的问题，可以看到这里lambda函数的返回值一直在变。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">xx = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]:</span><br><span class="line">    xx.append(<span class="keyword">lambda</span>: i)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;a:&#x27;</span>, xx[<span class="number">0</span>]())</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> j <span class="keyword">in</span> xx:</span><br><span class="line">    <span class="built_in">print</span>(j())</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;b:&#x27;</span>, xx[<span class="number">0</span>]())</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> xx:</span><br><span class="line">    <span class="built_in">print</span>(i, i())</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;c:&#x27;</span>, xx[<span class="number">0</span>], xx[<span class="number">0</span>]())</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> [<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>]:</span><br><span class="line">    <span class="built_in">print</span>(i)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;d:&#x27;</span>, xx[<span class="number">0</span>], xx[<span class="number">0</span>]())</span><br></pre></td></tr></table></figure><p>输出如下</p><figure class="highlight nix"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="params">a:</span> <span class="number">3</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="params">b:</span> <span class="number">3</span></span><br><span class="line"><span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca30310<span class="operator">&gt;</span> <span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca30310<span class="operator">&gt;</span></span><br><span class="line"><span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca303a0<span class="operator">&gt;</span> <span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca303a0<span class="operator">&gt;</span></span><br><span class="line"><span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca30430<span class="operator">&gt;</span> <span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca30430<span class="operator">&gt;</span></span><br><span class="line"><span class="params">c:</span> <span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca30310<span class="operator">&gt;</span> <span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca30430<span class="operator">&gt;</span></span><br><span class="line"><span class="number">4</span></span><br><span class="line"><span class="number">5</span></span><br><span class="line"><span class="number">6</span></span><br><span class="line"><span class="params">d:</span> <span class="operator">&lt;</span>function main2.<span class="symbol">&lt;locals&gt;</span>.<span class="symbol">&lt;lambda&gt;</span> at <span class="number">0</span>x10ca30310<span class="operator">&gt;</span> <span class="number">6</span></span><br></pre></td></tr></table></figure><h2 id="循环变量泄露"><a href="#循环变量泄露" class="headerlink" title="循环变量泄露"></a>循环变量泄露</h2><p>由于Python没有块级作用域，所以循环会改变当前作用域变量的值，也就是循环变量泄露。<br><strong>注意</strong>：Python3中列表推导式循环变量不会泄露，Python2中和常规循环一样泄露。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">x = -<span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">7</span>):</span><br><span class="line">    <span class="keyword">if</span> x == <span class="number">6</span>:</span><br><span class="line">        <span class="built_in">print</span>(x, <span class="string">&#x27;: for x inside loop&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(x, <span class="string">&#x27;: x in global&#x27;</span>)</span><br></pre></td></tr></table></figure><p>输出如下</p><figure class="highlight basic"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">6 </span>: <span class="keyword">for</span> x inside loop</span><br><span class="line"><span class="symbol">6 </span>: x in global</span><br></pre></td></tr></table></figure><h2 id="闭包与延迟绑定"><a href="#闭包与延迟绑定" class="headerlink" title="闭包与延迟绑定"></a>闭包与延迟绑定</h2><p>再讲一下<strong>闭包</strong>，在一个内部函数中，对外部作用域的变量进行引用，(并且一般外部函数的返回值为内部函数)，那么内部函数就被认为是闭包。<br>这里所谓的引用可以也就是内部函数记住了<strong>变量的名称</strong>（而不是值，这个从ast语法树可以看出），而变量对应的值是会变化的。<br>如果在循环中定义闭包，引用的变量的值在循环结束才统一确定为最后一次循环时的值，也就是<strong>延迟绑定</strong>（lazy binding）。</p><p>所以下面的例子，<code>xx</code>的所有匿名函数的返回值均为<code>3</code></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">xx = <span class="selector-attr">[]</span></span><br><span class="line"><span class="keyword">for</span> <span class="selector-tag">i</span> <span class="keyword">in</span> <span class="selector-attr">[1,2,3]</span>:</span><br><span class="line">    xx<span class="selector-class">.append</span>(lambda: i)</span><br></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>再分析一开始的问题，这里的匿名函数引用了变量<code>i</code>，而<code>i</code>是全局变量，所以再次使用<code>i</code>作为循环变量时，列表中的匿名函数引用的值就被覆盖了。</p><p>正确做法：</p><ul><li>在独立的函数中定义闭包</li><li>闭包引用的变量应该是其他函数不可修改的</li><li>优先使用列表推导式</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://stackoverflow.com/questions/3611760/scoping-in-python-for-loops">https://stackoverflow.com/questions/3611760/scoping-in-python-for-loops</a></li><li><a href="https://www.educative.io/courses/python-ftw-under-the-hood/N8RW8508RkL">https://www.educative.io/courses/python-ftw-under-the-hood/N8RW8508RkL</a></li><li><a href="https://mail.python.org/pipermail/python-ideas/2008-October/002109.html">https://mail.python.org/pipermail/python-ideas/2008-October/002109.html</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;循环变量泄露与延迟绑定叠加在一起，会产生一些让人迷惑的结果。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Python" scheme="https://blog.ponder.work/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>bash 语法备忘</title>
    <link href="https://blog.ponder.work/2021/11/28/bash-cheat-sheet/"/>
    <id>https://blog.ponder.work/2021/11/28/bash-cheat-sheet/</id>
    <published>2021-11-28T15:00:00.000Z</published>
    <updated>2022-10-17T15:44:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>bash 语法作为程序员好像都了解一些，但又缺少体系化学习，需要使用到某些功能时又经常手忙脚乱地查。<br>本文主要参考<a href="https://wangdoc.com/bash/">阮一峰的bash教程</a>，对bash的知识点进行了梳理。<br>本文目的是作为bash的语法备忘录、语法速查表。</p><span id="more"></span><h2 id="模式扩展"><a href="#模式扩展" class="headerlink" title="模式扩展"></a>模式扩展</h2><p>模式扩展（globbing），类似C语言中的宏展开，我们通常使用的通配符<code>*</code>就是其中之一。</p><p>Bash 一共提供八种扩展，前4种为文件扩展，只有文件路径确实存在才会扩展。</p><ul><li><code>~</code> 波浪线扩展</li><li><code>?</code> 问号扩展</li><li><code>*</code> 星号扩展</li><li><code>[]</code> 方括号扩展</li><li><code>{}</code> 大括号扩展</li><li><code>$var</code> 变量扩展</li><li><code>$(date)</code> 命令扩展</li><li><code>$((1 + 1))</code> 算术扩展</li></ul><h3 id="波浪线扩展"><a href="#波浪线扩展" class="headerlink" title="波浪线扩展"></a>波浪线扩展</h3><p>波浪线<code>~</code>会自动扩展成当前用户的主目录。<br><code>~user</code>表示扩展成用户<code>user</code>的主目录。如果用户不存在，则波浪号扩展不起作用。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ <span class="built_in">echo</span> ~/projects/</span><br><span class="line">/Users/ruan/projects/</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="built_in">echo</span> ~root/.ssh</span><br><span class="line">/var/root/.ssh</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="built_in">echo</span> ~aaa/.ssh</span><br><span class="line">~aaa/.ssh</span><br></pre></td></tr></table></figure><h3 id="问号扩展"><a href="#问号扩展" class="headerlink" title="问号扩展"></a>问号扩展</h3><p><code>?</code>字符代表文件路径里面的任意单个字符，不包括空字符。<br><strong>只有文件确实存在的前提下，才会发生扩展。</strong></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ touch &#123;a,b&#125;.txt ab.txt</span><br><span class="line"></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ ls ?.txt</span><br><span class="line"><span class="attribute">a</span>.txt  b.txt</span><br><span class="line"></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ ls ??.txt</span><br><span class="line"><span class="attribute">ab</span>.txt</span><br></pre></td></tr></table></figure><h3 id="星号扩展"><a href="#星号扩展" class="headerlink" title="星号扩展"></a>星号扩展</h3><p><code>*</code>字符代表文件路径里面的任意数量的任意字符，包括零个字符。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ <span class="built_in">ls</span> *.txt</span><br><span class="line">a.txt  ab.txt  b.txt</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="built_in">ls</span> /usr/local/Cellar/*/*/bin/z*</span><br><span class="line">/usr/local/Cellar/ffmpeg/4.4_2/bin/zmqsend</span><br><span class="line">/usr/local/Cellar/mysql-client/8.0.26/bin/zlib_decompress</span><br><span class="line">/usr/local/Cellar/netpbm/10.86.24/bin/zeisstopnm</span><br><span class="line">/usr/local/Cellar/perl/5.34.0/bin/zipdetails</span><br><span class="line">/usr/local/Cellar/zstd/1.5.0/bin/zstd</span><br></pre></td></tr></table></figure><h3 id="方括号扩展"><a href="#方括号扩展" class="headerlink" title="方括号扩展"></a>方括号扩展</h3><p>方括号扩展的形式是<code>[...]</code>，只有文件确实存在的前提下才会扩展。</p><p><code>[^...]</code>和<code>[!...]</code>。它们表示匹配不在方括号里面的字符</p><p>方括号扩展有一个简写形式<code>[start-end]</code>，表示匹配一个连续的范围</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">bash-<span class="number">5.1</span>$ ls <span class="selector-attr">[ab]</span><span class="selector-class">.txt</span></span><br><span class="line"><span class="selector-tag">a</span><span class="selector-class">.txt</span>  <span class="selector-tag">b</span><span class="selector-class">.txt</span></span><br><span class="line"></span><br><span class="line">bash-<span class="number">5.1</span>$ ls <span class="selector-attr">[^b]</span><span class="selector-tag">b</span><span class="selector-class">.txt</span></span><br><span class="line">ab<span class="selector-class">.txt</span></span><br><span class="line"></span><br><span class="line">bash-<span class="number">5.1</span>$ ls <span class="selector-attr">[a-b]</span><span class="selector-class">.txt</span></span><br><span class="line"><span class="selector-tag">a</span><span class="selector-class">.txt</span>  <span class="selector-tag">b</span>.txt</span><br></pre></td></tr></table></figure><h3 id="大括号扩展"><a href="#大括号扩展" class="headerlink" title="大括号扩展"></a>大括号扩展</h3><p>大括号扩展<code>{...}</code>表示分别扩展成大括号里面的所有值<br>大括号也可以与其他模式联用，并且总是先于其他模式进行扩展。</p><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ <span class="keyword">echo</span> &#123;1,2,3&#125;</span><br><span class="line">1 2 3</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="keyword">echo</span> a&#123;1,2,3&#125;b</span><br><span class="line">a1b a2b a3b</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="keyword">echo</span> <span class="params">--exclude=</span>&#123;a,b,c&#125;</span><br><span class="line"><span class="params">--exclude=a</span> <span class="params">--exclude=b</span> <span class="params">--exclude=c</span></span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="keyword">echo</span> foo&#123;1,2&#123;1,2&#125;0,3&#125;bar</span><br><span class="line">foo1bar foo210bar foo220bar foo3bar</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="keyword">echo</span> &#123;1.<span class="string">.3</span>&#125;</span><br><span class="line">1 2 3</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="keyword">echo</span> &#123;1.<span class="string">.10..2</span>&#125;</span><br><span class="line">1 3 5 7 9</span><br></pre></td></tr></table></figure><h3 id="变量扩展"><a href="#变量扩展" class="headerlink" title="变量扩展"></a>变量扩展</h3><p>Bash 将美元符号<code>$</code>开头的词元视为变量，将其扩展成变量值</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ <span class="built_in">echo</span> <span class="variable">$HOME</span></span><br><span class="line">/Users/ruan</span><br></pre></td></tr></table></figure><h3 id="命令扩展"><a href="#命令扩展" class="headerlink" title="命令扩展"></a>命令扩展</h3><p><code>$(...)</code>可以扩展成另一个命令的运行结果，该命令的所有输出都会作为返回值。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ <span class="built_in">echo</span> $(<span class="built_in">date</span>)</span><br><span class="line">日 11 28 16:22:09 CST 2021</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="built_in">echo</span> `<span class="built_in">date</span>`</span><br><span class="line">日 11 28 16:22:24 CST 2021</span><br></pre></td></tr></table></figure><h3 id="算术扩展"><a href="#算术扩展" class="headerlink" title="算术扩展"></a>算术扩展</h3><p><code>$((...))</code>可以扩展成整数运算的结果</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((<span class="number">1</span>+<span class="number">1</span>))</span><br><span class="line"><span class="attribute">2</span></span><br></pre></td></tr></table></figure><h2 id="引号使用"><a href="#引号使用" class="headerlink" title="引号使用"></a>引号使用</h2><h3 id="单引号"><a href="#单引号" class="headerlink" title="单引号"></a>单引号</h3><p>单引号用于保留字符的字面含义，在单引号里转义字符和模式扩展都会失效。</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> ls <span class="string">&#x27;[ab].txt&#x27;</span></span><br><span class="line">ls: cannot access <span class="string">&#x27;[ab].txt&#x27;</span>: <span class="keyword">No</span> such <span class="keyword">file</span> <span class="keyword">or</span> directory</span><br><span class="line"></span><br><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> ls <span class="string">&#x27;*&#x27;</span></span><br><span class="line">ls: cannot access <span class="string">&#x27;*&#x27;</span>: <span class="keyword">No</span> such <span class="keyword">file</span> <span class="keyword">or</span> directory</span><br></pre></td></tr></table></figure><h3 id="双引号"><a href="#双引号" class="headerlink" title="双引号"></a>双引号</h3><p>双引号比单引号宽松，三个特殊字符除外：美元符号（<code>$</code>）、反引号（<code>`</code>）和反斜杠（<code>\</code>）。这三个字符，会被 Bash 自动扩展。</p><p>也就是说，相比单引号在双引号中变量扩展，命令扩展，算术扩展以及转义字符是有效的。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ <span class="built_in">echo</span> <span class="string">&quot;<span class="subst">$((1+1)</span>)&quot;</span></span><br><span class="line">2</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$HOME</span>&quot;</span></span><br><span class="line">/Users/ruan</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="built_in">echo</span> <span class="string">&quot;<span class="subst">$(date)</span>&quot;</span></span><br><span class="line">日 11 28 16:35:27 CST 2021</span><br><span class="line"></span><br><span class="line">bash-5.1$ <span class="built_in">echo</span> -e <span class="string">&quot;1\t2&quot;</span></span><br><span class="line">12</span><br></pre></td></tr></table></figure><h3 id="引号嵌套"><a href="#引号嵌套" class="headerlink" title="引号嵌套"></a>引号嵌套</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 双引号中使用单引号</span></span><br><span class="line">bash<span class="literal">-5</span>.<span class="number">1</span><span class="variable">$</span> <span class="built_in">echo</span> <span class="string">&#x27;&quot;&#x27;</span></span><br><span class="line"><span class="string">&quot;</span></span><br><span class="line"><span class="string">bash-5.1<span class="variable">$</span> echo &#x27;&quot;</span><span class="variable">$</span>&#123;HOME&#125;<span class="string">&quot;&#x27;</span></span><br><span class="line"><span class="string">&quot;</span><span class="variable">$</span>&#123;HOME&#125;<span class="string">&quot;</span></span><br><span class="line"><span class="string"># 单引号中使用双引号</span></span><br><span class="line"><span class="string">bash-5.1<span class="variable">$</span> echo &quot;</span><span class="string">&#x27;$&#123;HOME&#125;&#x27;</span><span class="string">&quot;</span></span><br><span class="line"><span class="string">&#x27;/Users/ruan&#x27;</span></span><br><span class="line"><span class="string">bash-5.1<span class="variable">$</span> echo &quot;</span><span class="string">&#x27;\&quot;$&#123;HOME&#125;\&quot;&#x27;</span><span class="string">&quot;</span></span><br><span class="line"><span class="string">&#x27;&quot;</span>/Users/ruan<span class="string">&quot;&#x27;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># 引号嵌套中使用模式扩展，将需要扩展的字符放在单引号中；典型的有json变量填充</span></span><br><span class="line"><span class="string">bash-5.1<span class="variable">$</span> echo &#x27;&#123;&quot;</span>user<span class="string">&quot;: &quot;</span><span class="string">&#x27;$&#123;USER&#125;&#x27;</span><span class="string">&quot;&#125;&#x27;</span></span><br><span class="line"><span class="string">&#123;&quot;</span>user<span class="string">&quot;: &quot;</span>ruan<span class="string">&quot;&#125;</span></span><br></pre></td></tr></table></figure><h3 id="here-doc"><a href="#here-doc" class="headerlink" title="here doc"></a>here doc</h3><p>Here 文档（here document）是一种输入多行字符串的方法，格式如下。<br>它的格式分成开始标记（<code>&lt;&lt; token</code>）和结束标记（<code>token</code>）, 一般用字符串<code>EOF</code>作为token</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;&lt; token</span><br><span class="line">text</span><br><span class="line">token</span><br></pre></td></tr></table></figure><p>例如</p><figure class="highlight node-repl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ cat &lt;&lt; EOF</span><br><span class="line"><span class="meta prompt_">&gt;</span> <span class="language-javascript"><span class="number">11</span></span></span><br><span class="line"><span class="meta prompt_">&gt;</span> <span class="language-javascript"><span class="number">22</span></span></span><br><span class="line"><span class="meta prompt_">&gt;</span> <span class="language-javascript"><span class="number">33</span></span></span><br><span class="line"><span class="meta prompt_">&gt;</span> <span class="language-javascript"><span class="variable constant_">EOF</span></span></span><br><span class="line">11</span><br><span class="line">22</span><br><span class="line">33</span><br></pre></td></tr></table></figure><h3 id="here-string"><a href="#here-string" class="headerlink" title="here string"></a>here string</h3><p>Here 文档还有一个变体，叫做 Here 字符串（Here string），使用三个小于号（<code>&lt;&lt;&lt;</code>）表示。<br>它的作用是将字符串通过标准输入，传递给命令。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ <span class="built_in">cat</span> &lt;&lt;&lt; <span class="string">foobar</span></span><br><span class="line"><span class="string">foobar</span></span><br><span class="line">bash-5.1$ <span class="built_in">echo</span> foobar | <span class="built_in">cat</span></span><br><span class="line">foobar</span><br></pre></td></tr></table></figure><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><p>bash 是基于标准输入在不同进程间交互数据的，大部分功能都是在操作字符串，所以<strong>变量的默认类型也是字符串</strong>。</p><h3 id="声明变量和读取变量"><a href="#声明变量和读取变量" class="headerlink" title="声明变量和读取变量"></a>声明变量和读取变量</h3><p>声明时等号两边不能有空格。<br>Bash 变量名区分大小写，<code>HOME</code>和<code>home</code>是两个不同的变量。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ foo=<span class="number">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $foo</span><br><span class="line"><span class="attribute">1</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="变量查看和删除"><a href="#变量查看和删除" class="headerlink" title="变量查看和删除"></a>变量查看和删除</h3><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看所有变量, 其中包含父进程export的变量</span></span><br><span class="line">bash-5.1$ <span class="built_in">set</span></span><br><span class="line"><span class="attribute">UID</span>=501</span><br><span class="line"><span class="attribute">USER</span>=ruan</span><br><span class="line"><span class="attribute">bar</span>=2</span><br><span class="line"><span class="attribute">foo</span>=1</span><br><span class="line"></span><br><span class="line">bash-5.1$ unset foo</span><br><span class="line">bash-5.1$ <span class="built_in">set</span></span><br><span class="line"><span class="attribute">UID</span>=501</span><br><span class="line"><span class="attribute">USER</span>=ruan</span><br><span class="line"><span class="attribute">bar</span>=2</span><br></pre></td></tr></table></figure><h3 id="变量输出"><a href="#变量输出" class="headerlink" title="变量输出"></a>变量输出</h3><p>用户创建的变量仅可用于当前 Shell，子 Shell 默认读取不到父 Shell 定义的变量。<br>如果希望子进程能够读到这个变量，需要使用export命令。</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">bash-5.1$ bash -c <span class="built_in">set</span>  | grep foo</span><br><span class="line">bash-5.1$ <span class="built_in">export</span> <span class="attribute">foo</span>=1</span><br><span class="line">bash-5.1$ bash -c <span class="built_in">set</span>  | grep foo</span><br><span class="line"><span class="attribute">foo</span>=1</span><br></pre></td></tr></table></figure><h3 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h3><p>平时所说的环境变量，就是init进程export输出的。子进程对变量的修改不会影响父进程，也就是说变量不是共享的。</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看环境变量</span></span><br><span class="line">bash-5.1$ env</span><br><span class="line"><span class="attribute">SHELL</span>=/bin/zsh</span><br><span class="line"><span class="attribute">LSCOLORS</span>=Gxfxcxdxbxegedabagacad</span><br><span class="line"><span class="attribute">ITERM_PROFILE</span>=Default</span><br></pre></td></tr></table></figure><p>下面是一些常见的环境变量。</p><ul><li><code>BASHPID</code>：Bash 进程的进程 ID。</li><li><code>BASHOPTS</code>：当前 Shell 的参数，可以用<code>shopt</code>命令修改。</li><li><code>DISPLAY</code>：图形环境的显示器名字，通常是<code>:0</code>，表示 X Server 的第一个显示器。</li><li><code>EDITOR</code>：默认的文本编辑器。</li><li><code>HOME</code>：用户的主目录。</li><li><code>HOST</code>：当前主机的名称。</li><li><code>IFS</code>：词与词之间的分隔符，默认为空格。</li><li><code>LANG</code>：字符集以及语言编码，比如<code>zh_CN.UTF-8</code>。</li><li><code>PATH</code>：由冒号分开的目录列表，当输入可执行程序名后，会搜索这个目录列表。</li><li><code>PS1</code>：Shell 提示符。</li><li><code>PS2</code>： 输入多行命令时，次要的 Shell 提示符。</li><li><code>PWD</code>：当前工作目录。</li><li><code>RANDOM</code>：返回一个0到32767之间的随机数。</li><li><code>SHELL</code>：Shell 的名字。</li><li><code>SHELLOPTS</code>：启动当前 Shell 的<code>set</code>命令的参数</li><li><code>TERM</code>：终端类型名，即终端仿真器所用的协议。</li><li><code>UID</code>：当前用户的 ID 编号。</li><li><code>USER</code>：当前用户的用户名。</li></ul><h3 id="特殊变量"><a href="#特殊变量" class="headerlink" title="特殊变量"></a>特殊变量</h3><p>Bash 提供一些特殊变量。这些变量的值由 Shell 提供，用户不能进行赋值。</p><ul><li><code>$?</code>: 上一个命令的退出码, 0为成功，其他为失败</li><li><code>$$</code>: 当前进程的pid</li><li><code>$_</code>: 为上一个命令的最后一个参数</li><li><code>$!</code>: 为最近一个后台执行的异步命令的进程 ID。</li><li><code>$0</code>: bash脚本的参数列表，0是脚本文件路径，1到n是第1到第n个参数</li></ul><h3 id="变量默认值"><a href="#变量默认值" class="headerlink" title="变量默认值"></a>变量默认值</h3><ul><li><code>${varname:-word}</code>: 如果变量varname存在且不为空，则返回它的值，否则返回word</li><li><code>${varname:=word}</code>: 如果变量varname存在且不为空，则返回它的值，否则将它设为word，并且返回word。</li><li><code>${varname:+word}</code>: 如果变量名存在且不为空，则返回word，否则返回空值。它的目的是测试变量是否存在。</li><li><code>${varname:?message}</code>: 如果变量varname存在且不为空，则返回它的值，否则打印出varname: message，并中断脚本的执行。</li></ul><h3 id="declare-命令"><a href="#declare-命令" class="headerlink" title="declare 命令"></a>declare 命令</h3><p><code>declare</code>命令的主要参数（OPTION）如下。</p><ul><li><code>-a</code>：声明数组变量。</li><li><code>-A</code>：声明关联数组变量。</li><li><code>-f</code>：输出所有函数定义。</li><li><code>-F</code>：输出所有函数名。</li><li><code>-i</code>：声明整数变量。</li><li><code>-p</code>：查看变量信息。</li><li><code>-r</code>：声明只读变量。</li><li><code>-x</code>：该变量输出为环境变量。</li></ul><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p>bash 有字符串，数字，数字，关联数组四种数据类型，默认是字符串，其他类型需要手动声明。</p><h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><h4 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h4><p>语法 <code>varname=value</code></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ s1=abcdefg</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $s1</span><br><span class="line"><span class="attribute">abcdefg</span></span><br></pre></td></tr></table></figure><h4 id="获取长度（length）"><a href="#获取长度（length）" class="headerlink" title="获取长度（length）"></a>获取长度（length）</h4><p>语法 <code>${#varname}</code></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;#s1&#125;</span></span><br><span class="line"><span class="attribute">7</span></span><br></pre></td></tr></table></figure><h4 id="子字符串（substr）"><a href="#子字符串（substr）" class="headerlink" title="子字符串（substr）"></a>子字符串（substr）</h4><p>语法 <code>${varname:offset:length}</code>, offset为负数的时候，前面要加空格，防止与默认值语法冲突。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ s1=abcdefg</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;s1:1:3&#125;</span></span><br><span class="line"><span class="attribute">bcd</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;s1: -6:2&#125;</span></span><br><span class="line"><span class="attribute">bc</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;s1: -6:3&#125;</span></span><br><span class="line"><span class="attribute">bcd</span></span><br></pre></td></tr></table></figure><h4 id="替换-（replace）"><a href="#替换-（replace）" class="headerlink" title="替换 （replace）"></a>替换 （replace）</h4><h5 id="字符串头部的模式匹配"><a href="#字符串头部的模式匹配" class="headerlink" title="字符串头部的模式匹配"></a>字符串头部的模式匹配</h5><ul><li><code>${variable#pattern}</code>: 删除最短匹配（非贪婪匹配）的部分，返回剩余部分</li><li><code>${variable##pattern}</code>: 删除最长匹配（贪婪匹配）的部分，返回剩余部分</li></ul><p>匹配模式pattern可以使用<code>*</code>、<code>?</code>、<code>[]</code>等通配符。</p><figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$ </span>myPath=<span class="regexp">/home/cam</span><span class="regexp">/book/long</span>.file.name</span><br><span class="line"></span><br><span class="line"><span class="variable">$ </span>echo <span class="variable">$&#123;</span>myPath<span class="comment">#/*/&#125;</span></span><br><span class="line">cam/book/long.file.name</span><br><span class="line"></span><br><span class="line"><span class="variable">$ </span>echo <span class="variable">$&#123;</span>myPath<span class="comment">##/*/&#125;</span></span><br><span class="line">long.file.name</span><br></pre></td></tr></table></figure><h5 id="字符串尾部的模式匹配"><a href="#字符串尾部的模式匹配" class="headerlink" title="字符串尾部的模式匹配"></a>字符串尾部的模式匹配</h5><ul><li><code>${variable%pattern}</code>: 删除最短匹配（非贪婪匹配）的部分，返回剩余部分</li><li><code>${variable%%pattern}</code>: 删除最长匹配（贪婪匹配）的部分，返回剩余部分</li></ul><figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$ </span>path=<span class="regexp">/home/cam</span><span class="regexp">/book/long</span>.file.name</span><br><span class="line"></span><br><span class="line"><span class="variable">$ </span>echo <span class="variable">$&#123;</span>path%.*&#125;</span><br><span class="line"><span class="regexp">/home/cam</span><span class="regexp">/book/long</span>.file</span><br><span class="line"></span><br><span class="line"><span class="variable">$ </span>echo <span class="variable">$&#123;</span>path%%.*&#125;</span><br><span class="line"><span class="regexp">/home/cam</span><span class="regexp">/book/long</span></span><br></pre></td></tr></table></figure><h5 id="任意位置的模式匹配"><a href="#任意位置的模式匹配" class="headerlink" title="任意位置的模式匹配"></a>任意位置的模式匹配</h5><p>如果匹配<code>pattern</code>则用<code>replace</code>替换匹配的内容</p><ul><li><code>${variable/pattern/replace}</code>: 替换第一个匹配</li><li><code>${variable//pattern/replace}</code>: 替换所有匹配</li></ul><figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$ </span>path=<span class="regexp">/home/cam</span><span class="regexp">/foo/foo</span>.name</span><br><span class="line"></span><br><span class="line"><span class="variable">$ </span>echo <span class="variable">$&#123;</span>path/foo/bar&#125;</span><br><span class="line"><span class="regexp">/home/cam</span><span class="regexp">/bar/foo</span>.name</span><br><span class="line"></span><br><span class="line"><span class="variable">$ </span>echo <span class="variable">$&#123;</span>path/<span class="regexp">/foo/bar</span>&#125;</span><br><span class="line"><span class="regexp">/home/cam</span><span class="regexp">/bar/bar</span>.name</span><br></pre></td></tr></table></figure><h3 id="数字"><a href="#数字" class="headerlink" title="数字"></a>数字</h3><p>使用 <code>declare -i</code>声明整数变量。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 声明为整数，可以直接计算，不需要使用$符号</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ declare -i val1=<span class="number">12</span> val2=<span class="number">5</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $val1</span><br><span class="line"><span class="attribute">12</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ val1+=val2</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $val1</span><br><span class="line"><span class="attribute">17</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 一个变量声明为整数以后，依然可以被改写为字符串。Bash 不会报错，但会赋以不确定的值</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ val1=aaa</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $val1</span><br><span class="line"><span class="attribute">0</span></span><br></pre></td></tr></table></figure><h4 id="数值的进制"><a href="#数值的进制" class="headerlink" title="数值的进制"></a>数值的进制</h4><p>Bash 的数值默认都是十进制，但是在算术表达式中，也可以使用其他进制。</p><ul><li><code>number</code>：没有任何特殊表示法的数字是十进制数（以10为底）。</li><li><code>0number</code>：八进制数。</li><li><code>0xnumber</code>：十六进制数。</li><li><code>base#number</code>：<code>base</code>进制的数。</li></ul><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ declare -i a=<span class="number">0</span>x77</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $a</span><br><span class="line"><span class="attribute">119</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ declare -i a=<span class="number">0</span>xfe</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $a</span><br><span class="line"><span class="attribute">254</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ declare -i a=<span class="number">2</span>#<span class="number">111</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $a</span><br><span class="line"><span class="attribute">7</span></span><br></pre></td></tr></table></figure><h4 id="算术表达式"><a href="#算术表达式" class="headerlink" title="算术表达式"></a>算术表达式</h4><p><code>((...))</code>语法可以进行整数的算术运算。<br>支持的算术运算符如下。</p><ul><li><code>+</code>：加法</li><li><code>-</code>：减法</li><li><code>*</code>：乘法</li><li><code>/</code>：除法（整除）</li><li><code>%</code>：余数</li><li><code>**</code>：指数</li><li><code>++</code>：自增运算（前缀或后缀）</li><li><code>--</code>：自减运算（前缀或后缀）</li></ul><p>如果要读取算术运算的结果，需要在<code>((...))</code>前面加上美元符号<code>$((...))</code>，使其变成算术表达式，返回算术运算的值。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((<span class="number">1</span>+<span class="number">1</span>))</span><br><span class="line"><span class="attribute">2</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((<span class="number">1</span>-<span class="number">1</span>))</span><br><span class="line"><span class="attribute">0</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((<span class="number">1</span>*<span class="number">2</span>))</span><br><span class="line"><span class="attribute">2</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((<span class="number">1</span>/<span class="number">2</span>))</span><br><span class="line"><span class="attribute">0</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((<span class="number">5</span>%<span class="number">2</span>))</span><br><span class="line"><span class="attribute">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a=<span class="number">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((a++))</span><br><span class="line"><span class="attribute">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((a++))</span><br><span class="line"><span class="attribute">2</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $((++a))</span><br><span class="line"><span class="attribute">4</span></span><br></pre></td></tr></table></figure><h3 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h3><h4 id="创建数组"><a href="#创建数组" class="headerlink" title="创建数组"></a>创建数组</h4><p><code>array=(item1 item2)</code> 语法可初始化数组，括号内可以换行，多行初始化可以用<code>#</code>注释。</p><figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 直接初始化数组</span></span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>a=(<span class="number">1</span> <span class="number">2</span> <span class="number">3</span>)</span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>echo <span class="variable">$&#123;</span>a[@]&#125;</span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 多行初始化数组</span></span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>a=(</span><br><span class="line">&gt; <span class="number">1</span></span><br><span class="line">&gt; <span class="number">2</span></span><br><span class="line">&gt; <span class="number">3</span></span><br><span class="line">&gt; <span class="comment">#4</span></span><br><span class="line">&gt; )</span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>echo <span class="variable">$&#123;</span>a[@]&#125;</span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 模式扩展初始化</span></span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>a=(&#123;<span class="number">1</span>..<span class="number">3</span>&#125;)</span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>echo <span class="variable">$&#123;</span>a[@]&#125;</span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">3</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># declare -a命令声明一个数组，也是可以的。</span></span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>declare -a b</span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>b+=(<span class="number">1</span>)</span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>echo <span class="variable">$&#123;</span>b[@]&#125;</span><br><span class="line"><span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#read -a命令则是将用户的命令行输入，存入一个数组。</span></span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>read -a c</span><br><span class="line"><span class="number">11</span> <span class="number">22</span> <span class="number">33</span></span><br><span class="line">bash-<span class="number">5.1</span><span class="variable">$ </span>echo <span class="variable">$&#123;</span>c[@]&#125;</span><br><span class="line"><span class="number">11</span> <span class="number">22</span> <span class="number">33</span></span><br></pre></td></tr></table></figure><h4 id="访问数组元素"><a href="#访问数组元素" class="headerlink" title="访问数组元素"></a>访问数组元素</h4><p><code>array[index]</code> 语法可访问数组元素，不带index访问则是访问数组首个元素。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a=(<span class="number">1</span> <span class="number">2</span> <span class="number">3</span>)</span><br><span class="line"><span class="comment"># 查看元素</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[1]&#125;</span></span><br><span class="line"><span class="attribute">2</span></span><br><span class="line"><span class="comment"># 元素赋值</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a[<span class="number">1</span>]+=<span class="number">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[1]&#125;</span></span><br><span class="line"><span class="attribute">21</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a[<span class="number">1</span>]=<span class="number">22</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[1]&#125;</span></span><br><span class="line"><span class="attribute">22</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a[<span class="number">0</span>]=<span class="number">1111</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a&#125;</span></span><br><span class="line"><span class="attribute">1111</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[0]&#125;</span></span><br><span class="line"><span class="attribute">1111</span></span><br></pre></td></tr></table></figure><h4 id="数组长度"><a href="#数组长度" class="headerlink" title="数组长度"></a>数组长度</h4><p><code>${#array[@]}</code> 和 <code>${#array[*]}</code> 可访问获得数组长度</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a=(<span class="number">1</span> <span class="number">2</span> <span class="number">3</span>)</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;#a[*]&#125;</span></span><br><span class="line"><span class="attribute">3</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;#a[@]&#125;</span></span><br><span class="line"><span class="attribute">3</span></span><br></pre></td></tr></table></figure><h4 id="获取非空元素下标"><a href="#获取非空元素下标" class="headerlink" title="获取非空元素下标"></a>获取非空元素下标</h4><p><code>${!array[@]}</code> 或 <code>${!array[*]}</code>, 可以获得非空元素的下标</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a=(<span class="number">1</span> <span class="number">2</span> <span class="number">3</span>)</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a[<span class="number">6</span>]=<span class="number">6</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[0]&#125;</span></span><br><span class="line"><span class="attribute">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]&#125;</span></span><br><span class="line"><span class="attribute">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">6</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[3]&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[4]&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;!a[@]&#125;</span></span><br><span class="line"><span class="attribute">0</span> <span class="number">1</span> <span class="number">2</span> <span class="number">6</span></span><br><span class="line"><span class="comment"># 注意此时数组长度为4，并不是7</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;#a[@]&#125;</span></span><br><span class="line"><span class="attribute">4</span></span><br></pre></td></tr></table></figure><h4 id="数组元素切片"><a href="#数组元素切片" class="headerlink" title="数组元素切片"></a>数组元素切片</h4><p><code>${array[@]:position:length}</code>的语法可以提取数组成员。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a=(&#123;<span class="number">1</span>..<span class="number">10</span>&#125;)</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]&#125;</span></span><br><span class="line"><span class="attribute">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> <span class="number">6</span> <span class="number">7</span> <span class="number">8</span> <span class="number">9</span> <span class="number">10</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]:1:3&#125;</span></span><br><span class="line"><span class="attribute">2</span> <span class="number">3</span> <span class="number">4</span></span><br></pre></td></tr></table></figure><h4 id="数组追加元素"><a href="#数组追加元素" class="headerlink" title="数组追加元素"></a>数组追加元素</h4><p>数组末尾追加元素，可以使用<code>+=</code>赋值运算符。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a=(&#123;<span class="number">1</span>..<span class="number">10</span>&#125;)</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]&#125;</span></span><br><span class="line"><span class="attribute">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> <span class="number">6</span> <span class="number">7</span> <span class="number">8</span> <span class="number">9</span> <span class="number">10</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a+=(<span class="number">11</span> <span class="number">12</span>)</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]&#125;</span></span><br><span class="line"><span class="attribute">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> <span class="number">6</span> <span class="number">7</span> <span class="number">8</span> <span class="number">9</span> <span class="number">10</span> <span class="number">11</span> <span class="number">12</span></span><br></pre></td></tr></table></figure><h4 id="删除元素"><a href="#删除元素" class="headerlink" title="删除元素"></a>删除元素</h4><p>删除一个数组成员，使用<code>unset</code>命令。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a=(<span class="number">1</span> <span class="number">2</span> <span class="number">3</span>)</span><br><span class="line"><span class="comment"># 删除单个元素</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ unset a[<span class="number">1</span>]</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]&#125;</span></span><br><span class="line"><span class="attribute">1</span> <span class="number">3</span></span><br><span class="line"><span class="comment"># 删除整个数组</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ unset a</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]&#125;</span></span><br></pre></td></tr></table></figure><h3 id="关联数组"><a href="#关联数组" class="headerlink" title="关联数组"></a>关联数组</h3><p><code>declare -A</code>可以声明关联数组，关联数组使用字符串而不是整数作为数组索引。</p><p>除了初始化外，使用方法和数组基本相同</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ declare -A a</span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a[&#x27;red&#x27;]=<span class="number">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ a[&#x27;blue&#x27;]=<span class="number">2</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]&#125;</span></span><br><span class="line"><span class="attribute">2</span> <span class="number">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="variable">$&#123;a[@]:0:1&#125;</span></span><br><span class="line"><span class="attribute">2</span></span><br></pre></td></tr></table></figure><h2 id="控制流"><a href="#控制流" class="headerlink" title="控制流"></a>控制流</h2><h3 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h3><p><code>#</code>表示注释，每行从<code>#</code>开始之后的内容代表注释，会被bash忽略.</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo <span class="number">1111</span> # <span class="number">222</span></span><br><span class="line"><span class="attribute">1111</span></span><br></pre></td></tr></table></figure><h3 id="条件判断"><a href="#条件判断" class="headerlink" title="条件判断"></a>条件判断</h3><p>bash 和常规编程语言一样使用<code>if</code>作为分支条件的关键字, <code>fi</code>作为结束的关键字，<code>else</code>和<br><code>elif</code>子句是可选的</p><p>其中<code>if</code>和<code>elif</code>的<code>condition</code>所判断的内容是命令的<a href="#%E7%8A%B6%E6%80%81%E7%A0%81">状态码</a>是否为0，为0则执行关联的语句。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">因为bash中分号(;)和换行是等价的，所以有下面两种风格，其他多行语句也是类似的</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">本人偏好风格1</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">风格1</span></span><br><span class="line">if condition; then</span><br><span class="line">    command</span><br><span class="line">elif condition; then</span><br><span class="line">    command</span><br><span class="line">else</span><br><span class="line">    command</span><br><span class="line">fi</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">风格2</span></span><br><span class="line">if condition</span><br><span class="line">then</span><br><span class="line">    command</span><br><span class="line">elif condition</span><br><span class="line">then</span><br><span class="line">    command</span><br><span class="line">else</span><br><span class="line">    command</span><br><span class="line">fi</span><br></pre></td></tr></table></figure><p>这里的<code>condition</code>可以是多个命令，如<code>command1 &amp;&amp; command2</code>，或者<code>command1 || command2</code>，则if判断的是这两个命令的状态码的逻辑计算结果。</p><p><code>condition</code>也是可以是<code>command1; command2</code>， 则则if判断的是最后一个命令的状态码。</p><p>这里最常用的<code>condition</code>是<code>test</code>命令, 也就是<code>[[]]</code>和<code>[]</code>. <code>test</code>是bash的内置命令，会执行给定的表达式，结果为真满足则返回状态码0, 否则返回状态码1.</p><p>下文循环语言的<code>condition</code>也是相同的，就不赘述了</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ test <span class="number">1</span> -eq <span class="number">1</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $?</span><br><span class="line"><span class="attribute">0</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ test <span class="number">1</span> -eq <span class="number">2</span></span><br><span class="line"><span class="attribute">bash</span>-<span class="number">5</span>.<span class="number">1</span>$ echo $?</span><br><span class="line"><span class="attribute">1</span></span><br></pre></td></tr></table></figure><p><code>[[]]</code>和<code>[]</code>的区别是<code>[[]]</code>内部支持<code>&amp;&amp;</code>，<code>||</code>逻辑判断，所以以下三种写法是等价的。</p><p>由于<code>[</code>和<code>]</code>是命令， 所以两侧一定要有空格，也是就是<code>[ 1 -eq 1 ]</code>，否则bash会认为命令找不到。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="built_in">test</span></span></span><br><span class="line">if test 1 -eq 2 || test 1 -eq 1; then</span><br><span class="line">    echo True</span><br><span class="line">fi</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">[ ]</span></span><br><span class="line">if [ 1 -eq 2 ] || [ 1 -eq 1 ]; then</span><br><span class="line">    echo True</span><br><span class="line">fi</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">[[ ]]</span></span><br><span class="line">if [[ 1 -eq 2  || 1 -eq 1 ]]; then</span><br><span class="line">    echo True</span><br><span class="line">fi</span><br></pre></td></tr></table></figure><h4 id="逻辑操作符"><a href="#逻辑操作符" class="headerlink" title="逻辑操作符"></a>逻辑操作符</h4><p>判断条件支持且（&amp;&amp;）或（||）非（!）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">not</span>  </span><br><span class="line">if [[ ! &#x27;aa&#x27; == &#x27;bb&#x27; ]]; then</span><br><span class="line">    echo True</span><br><span class="line">fi</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">or</span> </span><br><span class="line">if [[ 1 -eq 2  || 1 -eq 1 ]]; then</span><br><span class="line">    echo True</span><br><span class="line">fi</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">and</span></span><br><span class="line">if [[ 1 -ne 2  &amp;&amp; 1 -eq 1 ]]; then</span><br><span class="line">    echo True</span><br><span class="line">fi</span><br></pre></td></tr></table></figure><h4 id="判断时引号使用（quote）"><a href="#判断时引号使用（quote）" class="headerlink" title="判断时引号使用（quote）"></a>判断时引号使用（quote）</h4><p>使用<code>[</code>和<code>test</code>时，变量引用注意加双引号，否则得不到正确的结果，<code>[[</code>则不需要。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">bash-3.2$ echo &quot;$SSH_CLIENT&quot;</span><br><span class="line"></span><br><span class="line">bash-3.2$ if [ -n $SSH_CLIENT ]; then echo 1; else echo 0; fi</span><br><span class="line">1</span><br><span class="line">bash-3.2$ if [ -n &quot;$SSH_CLIENT&quot; ]; then echo 1; else echo 0; fi</span><br><span class="line">0</span><br><span class="line">bash-3.2$ if [[ -n $SSH_CLIENT ]]; then echo 1; else echo 0; fi</span><br><span class="line">0</span><br></pre></td></tr></table></figure><h4 id="字符串判断"><a href="#字符串判断" class="headerlink" title="字符串判断"></a>字符串判断</h4><p>bash默认数据类型为字符串，所以常见的 <code>&gt;</code>, <code>&lt;</code>是用于字符串判断。</p><p>注意：字符串判断不支持<code>&gt;=</code>和<code>&lt;=</code>, 得使用逻辑组合来替代</p><ul><li><code>-z string</code>：字符串串长度为0</li><li><code>-n string</code>: 字符串长度大于0</li><li><code>string1 == string2</code>: string1 等于 string2</li><li><code>string1 = string2</code>: string1 等于 string2</li><li><code>string1 &gt; string2</code>: 如果按照字典顺序string1排列在string2之后</li><li><code>string1 &lt; string2</code>: 如果按照字典顺序string1排列在string2之前</li></ul><h4 id="数字-整数-判断"><a href="#数字-整数-判断" class="headerlink" title="数字(整数)判断"></a>数字(整数)判断</h4><p>下面的表达式用于判断整数。</p><ul><li><code>[ integer1 -eq integer2 ]</code>：如果<code>integer1</code>等于<code>integer2</code>，则为<code>true</code>。</li><li><code>[ integer1 -ne integer2 ]</code>：如果<code>integer1</code>不等于<code>integer2</code>，则为<code>true</code>。</li><li><code>[ integer1 -le integer2 ]</code>：如果<code>integer1</code>小于或等于<code>integer2</code>，则为<code>true</code>。</li><li><code>[ integer1 -lt integer2 ]</code>：如果<code>integer1</code>小于<code>integer2</code>，则为<code>true</code>。</li><li><code>[ integer1 -ge integer2 ]</code>：如果<code>integer1</code>大于或等于<code>integer2</code>，则为<code>true</code>。</li><li><code>[ integer1 -gt integer2 ]</code>：如果<code>integer1</code>大于<code>integer2</code>，则为<code>true</code>。</li></ul><h4 id="文件判断"><a href="#文件判断" class="headerlink" title="文件判断"></a>文件判断</h4><p>以下表达式用来判断文件状态。仅列举常用判断，详细支持列表参考 <a href="https://tldp.org/LDP/abs/html/fto.html">https://tldp.org/LDP/abs/html/fto.html</a></p><ul><li><code>[ -a file ]</code>：如果 file 存在，则为<code>true</code>。</li><li><code>[ -d file ]</code>：如果 file 存在并且是一个目录，则为<code>true</code>。</li><li><code>[ -e file ]</code>：如果 file 存在，则为<code>true</code>, 同<code>-a</code>。</li><li><code>[ -f file ]</code>：如果 file 存在并且是一个普通文件，则为<code>true</code>。</li><li><code>[ -h file ]</code>：如果 file 存在并且是符号链接，则为<code>true</code>。</li><li><code>[ -L file ]</code>：如果 file 存在并且是符号链接，则为<code>true</code>, 同<code>-h</code>。</li><li><code>[ -p file ]</code>：如果 file 存在并且是一个命名管道，则为<code>true</code>。</li><li><code>[ -r file ]</code>：如果 file 存在并且可读（当前用户有可读权限），则为<code>true</code>。</li><li><code>[ -s file ]</code>：如果 file 存在且其长度大于零，则为<code>true</code>。</li><li><code>[ -w file ]</code>：如果 file 存在并且可写（当前用户拥有可写权限），则为<code>true</code>。</li><li><code>[ -x file ]</code>：如果 file 存在并且可执行（有效用户有执行／搜索权限），则为<code>true</code>。</li></ul><h3 id="switch-case"><a href="#switch-case" class="headerlink" title="switch case"></a>switch case</h3><p>bash也支持，switch case，语法如下。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">case EXPRESSION in</span><br><span class="line">  PATTERN_1)</span><br><span class="line">    STATEMENTS</span><br><span class="line">    ;;</span><br><span class="line">  PATTERN_2)</span><br><span class="line">    STATEMENTS</span><br><span class="line">    ;;</span><br><span class="line">  PATTERN_N)</span><br><span class="line">    STATEMENTS</span><br><span class="line">    ;;</span><br><span class="line">  *)</span><br><span class="line">    STATEMENTS</span><br><span class="line">    ;;</span><br><span class="line">esac</span><br></pre></td></tr></table></figure><p>例如</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">a=2</span><br><span class="line"><span class="keyword">case</span> <span class="variable">$a</span> <span class="keyword">in</span></span><br><span class="line">1)</span><br><span class="line">    <span class="built_in">echo</span> 11</span><br><span class="line">    ;;</span><br><span class="line">2)</span><br><span class="line">    <span class="built_in">echo</span> 22</span><br><span class="line">    ;;</span><br><span class="line">*)</span><br><span class="line">    ;;</span><br><span class="line"><span class="keyword">esac</span></span><br></pre></td></tr></table></figure><h3 id="循环"><a href="#循环" class="headerlink" title="循环"></a>循环</h3><h4 id="while-循环"><a href="#while-循环" class="headerlink" title="while 循环"></a>while 循环</h4><p><code>while</code>循环有一个判断条件，只要符合条件，就不断循环执行指定的语句。<br><code>condition</code>与if语句的相同，就不赘述了。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">while condition; do</span><br><span class="line">  command</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h4 id="unitl-循环"><a href="#unitl-循环" class="headerlink" title="unitl 循环"></a>unitl 循环</h4><p><code>until</code>循环与<code>while</code>循环恰好相反，只要不符合判断条件（判断条件失败），就不断循环执行指定的语句。一旦符合判断条件，就退出循环。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">until condition; do</span><br><span class="line">  command</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h4 id="for-in-循环"><a href="#for-in-循环" class="headerlink" title="for-in 循环"></a>for-in 循环</h4><p><code>for...in</code>循环用于遍历列表的每一项。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for variable in list; do</span><br><span class="line">  commands</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p>常见的几种用法</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">for i in 1 2 3; do</span><br><span class="line">    echo $i</span><br><span class="line">done</span><br><span class="line"></span><br><span class="line">for i in &#123;1..3&#125;; do</span><br><span class="line">    echo $i</span><br><span class="line">done</span><br><span class="line"></span><br><span class="line">list=(1 2 3)</span><br><span class="line">for i in $&#123;list[@]&#125;; do</span><br><span class="line">    echo $i</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h4 id="for-循环"><a href="#for-循环" class="headerlink" title="for 循环"></a>for 循环</h4><p><code>for</code>循环还支持 C 语言的循环语法。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for (( expression1; expression2; expression3 )); do</span><br><span class="line">  commands</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p>上面代码中，<code>expression1</code>用来初始化循环条件，<code>expression2</code>用来决定循环结束的条件，<code>expression3</code>在每次循环迭代的末尾执行，用于更新值。</p><p>注意，循环条件放在双重圆括号之中。另外，圆括号之中使用变量，不必加上美元符号<code>$</code>。</p><p>例如</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for ((i=1; i&lt;=3; i++)); do</span><br><span class="line">    echo $i</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h4 id="跳出循环"><a href="#跳出循环" class="headerlink" title="跳出循环"></a>跳出循环</h4><p>Bash 提供了两个内部命令<code>break</code>和<code>continue</code>，用来在循环内部跳出循环。</p><p><code>break</code>命令立即终止循环，程序继续执行循环块之后的语句，即不再执行剩下的循环。<br><code>continue</code>命令立即终止本轮循环，开始执行下一轮循环。</p><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><h3 id="函数定义"><a href="#函数定义" class="headerlink" title="函数定义"></a>函数定义</h3><p>Bash 函数定义的语法有两种，其中<code>fn</code>为定义的函数名称。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">第一种</span></span><br><span class="line">fn() &#123;</span><br><span class="line"><span class="meta prompt_">  # </span><span class="language-bash">codes</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">第二种</span></span><br><span class="line">function fn() &#123;</span><br><span class="line"><span class="meta prompt_">  # </span><span class="language-bash">codes</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="函数参数"><a href="#函数参数" class="headerlink" title="函数参数"></a>函数参数</h3><p>函数体内可以使用参数变量，获取函数参数。函数的参数变量，与脚本参数变量是一致的。</p><ul><li><code>${N}</code>：函数的第一个到第N个的参数。</li><li><code>$0</code>：函数所在的脚本名。</li><li><code>$#</code>：函数的参数总数。</li><li><code>$@</code>：函数的全部参数，参数之间使用空格分隔。</li><li><code>$*</code>：函数的全部参数，参数之间使用变量<code>$IFS</code>值的第一个字符分隔，默认为空格，但是可以自定义。</li></ul><h3 id="函数调用"><a href="#函数调用" class="headerlink" title="函数调用"></a>函数调用</h3><p><code>funcname arg1 arg ... argN</code> 的语法进行函数调用。主要函数的返回值和输出值（标准输出）的区别，这和主流编程语言不同</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">add() &#123;</span><br><span class="line">    declare -i res</span><br><span class="line">    res=0</span><br><span class="line">    for i in $@; do</span><br><span class="line">        res+=$i</span><br><span class="line">    done</span><br><span class="line">    echo $res</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">结果为10</span></span><br><span class="line">add 1 2 3 4</span><br></pre></td></tr></table></figure><h3 id="函数返回值"><a href="#函数返回值" class="headerlink" title="函数返回值"></a>函数返回值</h3><p><code>return</code>命令用于从函数返回一个值。返回值和命令的状态码一样，可以用<code>$?</code>拿到值。<br><code>return</code>也可以不接具体的值，则返回值是return命令的上一条命令的状态码。<br>如果不加<code>return</code>，则返回值是函数体最后一条命令的状态码。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">function func_return_value &#123;</span><br><span class="line">  return 10</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="关键概念"><a href="#关键概念" class="headerlink" title="关键概念"></a>关键概念</h2><h3 id="shebang"><a href="#shebang" class="headerlink" title="shebang"></a>shebang</h3><p>Shebang（也称为Hashbang）是一个由井号和叹号构成的字符序列<code>#!</code>， 其出现在可执行文本文件的第一行的前两个字符。<br>在文件中存在Shebang的情况下，类Unix操作系统的程序加载器会分析Shebang后的内容，将这些内容作为解释器指令，并调用该指令.</p><p>例如，shell脚本</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> Hello, world!</span><br></pre></td></tr></table></figure><p>python 脚本</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python -u</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Hello, world!&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="状态码"><a href="#状态码" class="headerlink" title="状态码"></a>状态码</h3><p>每个命令都会返回一个退出状态码（有时候也被称为返回状态）。</p><p>成功的命令返回 0，不成功的命令返回非零值，非零值通常都被解释成一个错误码。行为良好的 UNIX 命令、程序和工具都会返回 0 作为退出码来表示成功，虽然偶尔也会有例外。</p><p>状态码一般是程序的main函数的返回码，如c,c++。<br>如果是bash脚本，状态码的值则是 <code>exit</code> 命令的参数值。<br>当脚本以不带参数的 <code>exit</code> 命令来结束时，脚本的退出状态码就由脚本中最后执行的命令来决定，这与函数的 <code>return</code> 行为是一致的。</p><p>特殊变量<code>$?</code>可以查看上个命令的退出状态码</p><h3 id="文件描述符"><a href="#文件描述符" class="headerlink" title="文件描述符"></a>文件描述符</h3><p>文件描述符在形式上是一个非负整数。指向内核为每一个进程所维护的该进程打开文件的记录表。<br>当程序打开一个现有文件或者创建一个新文件时，内核向进程返回一个文件描述符。</p><h4 id="标准输入输出"><a href="#标准输入输出" class="headerlink" title="标准输入输出"></a>标准输入输出</h4><p>每个Unix进程（除了可能的守护进程）应均有三个标准的POSIX文件描述符，对应于三个标准流：</p><ul><li><code>0</code>：标准输入</li><li><code>1</code>：标准输出</li><li><code>2</code>：错误输出</li></ul><h4 id="打开新的文件描述符"><a href="#打开新的文件描述符" class="headerlink" title="打开新的文件描述符"></a>打开新的文件描述符</h4><p>手动指定描述符</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">exec 3&lt;&gt; /tmp/foo  #open fd 3.</span><br><span class="line">echo &quot;test&quot; &gt;&amp;3</span><br><span class="line">exec 3&gt;&amp;- #close fd 3.</span><br></pre></td></tr></table></figure><p>系统自动分配描述符，bash4.1开始支持(在macos报错，原因不明)</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line"></span><br><span class="line">FILENAME=abc.txt</span><br><span class="line"></span><br><span class="line">exec &#123;FD&#125;&lt;&gt;&quot;$FILENAME&quot;</span><br><span class="line">echo 11 &gt;&amp;FD</span><br><span class="line">echo 22 &gt;&amp;FD</span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">FD&gt;&amp;-</span></span><br></pre></td></tr></table></figure><h4 id="描述符重定向"><a href="#描述符重定向" class="headerlink" title="描述符重定向"></a>描述符重定向</h4><ul><li><code>command &gt; file</code>: 将输出重定向到 file。</li><li><code>command &lt; file</code>: 将输入重定向到 file。</li><li><code>command &gt;&gt; file</code>: 将输出以追加的方式重定向到 file。</li><li><code>n &gt; file</code>: 将文件描述符为 n 的文件重定向到 file。</li><li><code>n &gt;&gt; file</code>: 将文件描述符为 n 的文件以追加的方式重定向到 file。</li><li><code>n &gt;&amp; m</code>: 将输出文件 m 和 n 合并。</li><li><code>n &lt;&amp; m</code>: 将输入文件 m 和 n 合并。</li></ul><p>所以命令中常见的<code>ls -al &gt; output.txt 2&gt;&amp;1</code>, 就是将标准输出和错误输出都重定向到一个文件。<br>等价于<code>ls -al &amp;&gt;output.txt</code>，本人偏好这种写法，比较简洁。</p><h3 id="IFS-Input-Field-Separators"><a href="#IFS-Input-Field-Separators" class="headerlink" title="IFS (Input Field Separators)"></a>IFS (Input Field Separators)</h3><p>IFS决定了bash在处理字符串的时候是如何进行单词切分。<br>IFS的默认值是空格，TAB，换行符，即<code> \t\n</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">echo</span> <span class="string">&quot;<span class="variable">$IFS</span>&quot;</span> | <span class="built_in">cat</span> -et</span></span><br><span class="line"> ^I$</span><br><span class="line"><span class="meta prompt_">$</span></span><br></pre></td></tr></table></figure><p>例如，在for循环的时候，如何区分每个item</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for i in `echo -e &quot;foo bar\tfoobar\nfoofoo&quot;`; do</span><br><span class="line">    echo &quot;&#x27;$i&#x27; is the substring&quot;;</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p>也可以自定义</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">OLD_IFS=&quot;$IFS&quot;</span><br><span class="line">IFS=&quot;:&quot;</span><br><span class="line">string=&quot;1:2:3&quot;</span><br><span class="line">for i in $string; do</span><br><span class="line">    echo &quot;&#x27;$i&#x27; is the substring&quot;;</span><br><span class="line">done</span><br><span class="line">IFS=$OLD_IFS</span><br></pre></td></tr></table></figure><h3 id="任务管理"><a href="#任务管理" class="headerlink" title="任务管理"></a>任务管理</h3><p>linux进程分前台（fg）和后台（bg）。</p><p>在命令的末尾添加<code>&amp;</code>可以将命令后台执行，一般配合输出重定向使用。</p><p><code>jobs</code>可以查看当前bash进程的子进程，并通过<code>fg</code>和<code>bg</code>进行前台和后台切换。</p><p><code>%1</code>代表后台的第一个进程，以此类推<code>%N</code>代表第n个.</p><p><code>control + Z</code>可以将当前前台程序暂停，配合<code>bg</code>可以将其转后台。</p><p><code>wait [pid]</code>可以等待子进程结束，如果不带pid参数则等待所有子进程结束。</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> <span class="built_in">sleep</span> <span class="number">100</span></span><br><span class="line">^Z</span><br><span class="line">[<span class="number">1</span>]+  已停止               <span class="built_in">sleep</span> <span class="number">100</span></span><br><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> jobs</span><br><span class="line">[<span class="number">1</span>]+  已停止               <span class="built_in">sleep</span> <span class="number">100</span></span><br><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> bg %<span class="number">1</span></span><br><span class="line">[<span class="number">1</span>]+ <span class="built_in">sleep</span> <span class="number">100</span> &amp;</span><br><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> <span class="built_in">sleep</span> <span class="number">200</span> &amp;</span><br><span class="line">[<span class="number">2</span>] <span class="number">63603</span></span><br><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> jobs</span><br><span class="line">[<span class="number">1</span>]-  运行中               <span class="built_in">sleep</span> <span class="number">100</span> &amp;</span><br><span class="line">[<span class="number">2</span>]+  运行中               <span class="built_in">sleep</span> <span class="number">200</span> &amp;</span><br><span class="line">bash<span class="number">-5.1</span><span class="symbol">$</span> wait %<span class="number">1</span></span><br><span class="line">[<span class="number">1</span>]-  已完成               <span class="built_in">sleep</span> <span class="number">100</span></span><br></pre></td></tr></table></figure><h4 id="后台进程并发控制"><a href="#后台进程并发控制" class="headerlink" title="后台进程并发控制"></a>后台进程并发控制</h4><p>可以利用jobs对后台进程并发数目进行控制</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">for i in &#123;1..30&#125;; do</span><br><span class="line">sleep $((30+i)) &amp;</span><br><span class="line">if [[ $(jobs | wc -l ) -gt 10 ]]; then</span><br><span class="line">jobs</span><br><span class="line">wait</span><br><span class="line">fi</span><br><span class="line">done</span><br><span class="line">wait</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://wangdoc.com/bash/">https://wangdoc.com/bash/</a></li><li><a href="https://tldp.org/LDP/abs/html/fto.html">https://tldp.org/LDP/abs/html/fto.html</a></li><li><a href="https://zh.wikipedia.org/wiki/Shebang">https://zh.wikipedia.org/wiki/Shebang</a></li><li><a href="https://en.wikipedia.org/wiki/File_descriptor">https://en.wikipedia.org/wiki/File_descriptor</a></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;bash 语法作为程序员好像都了解一些，但又缺少体系化学习，需要使用到某些功能时又经常手忙脚乱地查。&lt;br&gt;本文主要参考&lt;a href=&quot;https://wangdoc.com/bash/&quot;&gt;阮一峰的bash教程&lt;/a&gt;，对bash的知识点进行了梳理。&lt;br&gt;本文目的是作为bash的语法备忘录、语法速查表。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="Shell" scheme="https://blog.ponder.work/tags/Shell/"/>
    
  </entry>
  
  <entry>
    <title>MySQL 自定义数据库路径</title>
    <link href="https://blog.ponder.work/2021/10/14/mysql-custom-location/"/>
    <id>https://blog.ponder.work/2021/10/14/mysql-custom-location/</id>
    <published>2021-10-14T20:17:00.000Z</published>
    <updated>2021-10-15T00:05:43.000Z</updated>
    
    <content type="html"><![CDATA[<p><em>最近的一些文章是整理以前的笔记</em><br>MySQL 是最常用的数据，有时希望将数据库文件存放在自定义路径，或者在系统中启动多个 MySQL服务。</p><span id="more"></span><p>当然，如果条件允许，建议直接使用 docker </p><h2 id="创建-my-cnf-配置文件"><a href="#创建-my-cnf-配置文件" class="headerlink" title="创建 my.cnf 配置文件"></a>创建 my.cnf 配置文件</h2><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[mysqld]</span></span><br><span class="line"><span class="attr">datadir</span>=/home/ruan/data/mysql_data</span><br><span class="line"><span class="attr">socket</span>=/home/ruan/data/mysql.sock</span><br><span class="line"><span class="attr">user</span>=ruan</span><br><span class="line"><span class="comment"># Disabling symbolic-links is recommended to prevent assorted security risks</span></span><br><span class="line"><span class="attr">symbolic-links</span>=<span class="number">0</span></span><br><span class="line"><span class="attr">bind-address</span>=<span class="number">127.0</span>.<span class="number">0.1</span></span><br><span class="line"><span class="attr">port</span> = <span class="number">12345</span></span><br><span class="line"></span><br><span class="line"><span class="attr">character-set-server</span>=utf8</span><br><span class="line"><span class="attr">collation-server</span>=utf8_general_ci</span><br><span class="line"></span><br><span class="line"><span class="section">[mysqld_safe]</span></span><br><span class="line"><span class="attr">log-error</span>=/home/ruan/data/mysqld.log</span><br><span class="line"><span class="attr">pid-file</span>=/home/ruan/data/mysqld.pid</span><br></pre></td></tr></table></figure><h2 id="启动和初始化"><a href="#启动和初始化" class="headerlink" title="启动和初始化"></a>启动和初始化</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">启动MySQL</span></span><br><span class="line">mysqld_safe --defaults-file=my.cnf --user=ruan</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">初始化数据库</span></span><br><span class="line">mysql_install_db --defaults-file=my.cnf --user=ruan</span><br></pre></td></tr></table></figure><h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ tree <span class="class"><span class="keyword">data</span></span></span><br><span class="line"><span class="class"><span class="keyword">data</span></span></span><br><span class="line">├── my.cnf</span><br><span class="line">├── mysql_data</span><br><span class="line">│   ├── ibdata1</span><br><span class="line">│   ├── ib_logfile0</span><br><span class="line">│   ├── ib_logfile1</span><br><span class="line">├── mysqld.log</span><br><span class="line">├── mysqld.pid</span><br><span class="line">└── mysql.sock</span><br></pre></td></tr></table></figure><h2 id="设置密码"><a href="#设置密码" class="headerlink" title="设置密码"></a>设置密码</h2><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; use mysql; <span class="keyword">update</span> <span class="keyword">user</span> <span class="keyword">set</span> <span class="keyword">password</span>=<span class="keyword">password</span>(<span class="string">&#x27;m654321&#x27;</span>) <span class="keyword">where</span> <span class="keyword">user</span>=<span class="string">&#x27;root&#x27;</span>; flush <span class="keyword">privileges</span>;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;em&gt;最近的一些文章是整理以前的笔记&lt;/em&gt;&lt;br&gt;MySQL 是最常用的数据，有时希望将数据库文件存放在自定义路径，或者在系统中启动多个 MySQL服务。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="MySQL" scheme="https://blog.ponder.work/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>hexo 站内搜索内容不完全问题修复</title>
    <link href="https://blog.ponder.work/2021/10/11/hexo-local-search-not-complete-fix/"/>
    <id>https://blog.ponder.work/2021/10/11/hexo-local-search-not-complete-fix/</id>
    <published>2021-10-11T20:00:00.000Z</published>
    <updated>2021-10-12T03:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>在使用 Hexo 的站内搜索时，发现搜索的内容不全。单步调试发现xml解析不完整，有部分内容被截断了。</p><p>在浏览器中打开<a href="/search.xml">&#x2F;search.xml</a>发现以下错误。显然xml中有非法字符，xml解析产生了错误。</p><span id="more"></span><p><img data-src="https://image.ponder.work/mweb/2021-10-12-16339770374988.jpg"></p><p>将search.xml文件保存，并用python打开，找到具体出错的位置。<br>utf8解码之后可以发现<code>\x10</code>非法字符，将其删除，重新生成文章问题解决。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>xxx = <span class="built_in">open</span>(<span class="string">&#x27;./tmp.xml&#x27;</span>, <span class="string">&#x27;rb&#x27;</span>).read()</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>xxx.index(<span class="string">b&#x27;\x10\xE7\x84\xB6&#x27;</span>)</span><br><span class="line"><span class="number">923278</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>xxx[<span class="number">923278</span>:<span class="number">923278</span>+<span class="number">31</span>]</span><br><span class="line"><span class="string">b&#x27;\x10\xe7\x84\xb6\xe5\x88\x99\xef\xbc\x8c\xe4\xbb\x8a\xe4\xb9\x8b\xe4\xb8\x96\xe4\xba\xba\xef\xbc\x8c\xe7\xb1\xbb\xe6\xad\xa4&#x27;</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>xxx[<span class="number">923278</span>:<span class="number">923278</span>+<span class="number">31</span>].decode()</span><br><span class="line"><span class="string">&#x27;\x10然则，今之世人，类此&#x27;</span></span><br></pre></td></tr></table></figure><p><img data-src="https://image.ponder.work/mweb/2021-10-12-16339793845815.jpg"></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在使用 Hexo 的站内搜索时，发现搜索的内容不全。单步调试发现xml解析不完整，有部分内容被截断了。&lt;/p&gt;
&lt;p&gt;在浏览器中打开&lt;a href=&quot;/search.xml&quot;&gt;&amp;#x2F;search.xml&lt;/a&gt;发现以下错误。显然xml中有非法字符，xml解析产生了错误。&lt;/p&gt;</summary>
    
    
    
    <category term="编程" scheme="https://blog.ponder.work/categories/programming/"/>
    
    
    <category term="XML" scheme="https://blog.ponder.work/tags/XML/"/>
    
  </entry>
  
  <entry>
    <title>macOS 使用技巧</title>
    <link href="https://blog.ponder.work/2021/10/10/macos-tips/"/>
    <id>https://blog.ponder.work/2021/10/10/macos-tips/</id>
    <published>2021-10-10T15:00:00.000Z</published>
    <updated>2023-11-29T17:05:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>对程序员来说，macOS 就是一个桌面支持比较好的 Linux&#x2F;Unix，给日常开发带来了许多便利</p><p>本文记录一些日常使用中的小技巧。</p><span id="more"></span><h2 id="macOS-mojave-密码位数限制"><a href="#macOS-mojave-密码位数限制" class="headerlink" title="macOS mojave 密码位数限制"></a>macOS mojave 密码位数限制</h2><p>苹果在10.14的系统上增加了密码最小长度的限制<br>可以通过这个命令去除</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pwpolicy -clearaccountpolicies</span><br></pre></td></tr></table></figure><p><strong>注意</strong>：去除限制之后，通过time machine备份的系统，还原到有限制的机器上，可能会导致一些bug。如卡死在登陆页面，部分应用的资源库损坏等等。</p><h2 id="重置账户、配置"><a href="#重置账户、配置" class="headerlink" title="重置账户、配置"></a>重置账户、配置</h2><p>如果出现上面的问题，可以尝试重置下系统设置（选择任意一种，建议选1或2）</p><ol><li>新建管理员账号，在新机器上去除密码限制。</li><li>进入恢复模式（开机按command+r），删除对应用户账户配置文件（恢复模式下文件挂载路径可能不同）, 重新开机</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mv</span> ~/Library/Preferences ~/Library/Preferences.bak</span><br><span class="line"><span class="built_in">mv</span> ~/Library/PreferencePanes ~/Library/PreferencePanes.bak</span><br></pre></td></tr></table></figure><ol start="3"><li>清除机器配置状态，重新设置一个新账户（开机按住command+s；）</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">/sbin/mount -uaw</span><br><span class="line"><span class="built_in">rm</span> var/db/.applesetupdone</span><br><span class="line">reboot</span><br></pre></td></tr></table></figure><h2 id="Mac-开启任何来源选项"><a href="#Mac-开启任何来源选项" class="headerlink" title="Mac 开启任何来源选项"></a>Mac 开启任何来源选项</h2><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo spctl <span class="comment">--master-disable</span></span><br></pre></td></tr></table></figure><h2 id="让系统更流畅"><a href="#让系统更流畅" class="headerlink" title="让系统更流畅"></a>让系统更流畅</h2><p>macOS 10.14 以后系统动画越来越复杂，对显卡要求比较高，导致配置比较老的设备运行起来很卡顿。<br>特别是big sur系统，使用intel核显的机器都不建议安装。</p><p>通过关闭一些动画和特效可以让系统更流畅些</p><p><img data-src="https://image.ponder.work/mweb/2021-10-10-16338756772783.jpg"></p><h2 id="键位修改"><a href="#键位修改" class="headerlink" title="键位修改"></a>键位修改</h2><p>使用 <a href="https://karabiner-elements.pqrs.org/">karabiner</a> 可以对系统全局快捷键和应用快捷键进行修改。</p><p>通过配置 karabiner 将 command，control，option 按键进行重映射，可以让 macOS 的键位 和 Windows&#x2F;Linux 接近。</p><h2 id="使-macOS-的命令风格与Linux相同"><a href="#使-macOS-的命令风格与Linux相同" class="headerlink" title="使 macOS 的命令风格与Linux相同"></a>使 macOS 的命令风格与Linux相同</h2><p>macOS 的命令应该是 Unix 风格的，和 Linux 有些不同，可选参数必须放在前面，会有些不方便。<br>比如</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">mac</span></span><br><span class="line">➜  ~ /bin/ls /tmp -al</span><br><span class="line">ls: -al: No such file or directory</span><br></pre></td></tr></table></figure><p>可以安装 gnu 工具，覆盖这些命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">brew install coreutils findutils gnu-tar htop</span><br><span class="line"><span class="built_in">export</span>   PATH=<span class="string">&quot;/usr/local/opt/coreutils/libexec/gnubin:<span class="variable">$PATH</span>&quot;</span></span><br><span class="line"><span class="built_in">export</span>  MANPATH=<span class="string">&quot;/usr/local/opt/coreutils/libexec/gnuman:<span class="variable">$MANPATH</span>&quot;</span></span><br></pre></td></tr></table></figure><h2 id="清除一些-macOS-系统默认快捷键"><a href="#清除一些-macOS-系统默认快捷键" class="headerlink" title="清除一些 macOS 系统默认快捷键"></a>清除一些 macOS 系统默认快捷键</h2><p>创建文件 ~&#x2F;Library&#x2F;KeyBindings&#x2F;DefaultKeyBinding.dict<br>这里是清除了默认的 <code>Control + Command + 方向键</code>的行为</p><p>修改完成后必须重新打开现有app才能生效</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"><span class="string">&quot;^@\UF701&quot;</span> <span class="operator">=</span> (<span class="string">&quot;noop:&quot;</span>)<span class="comment">;</span></span><br><span class="line"><span class="string">&quot;^@\UF702&quot;</span> <span class="operator">=</span> (<span class="string">&quot;noop:&quot;</span>)<span class="comment">;</span></span><br><span class="line"><span class="string">&quot;^@\UF703&quot;</span> <span class="operator">=</span> (<span class="string">&quot;noop:&quot;</span>)<span class="comment">;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>字符含义</p><table><thead><tr><th>Prefix</th><th>Meaning</th></tr></thead><tbody><tr><td><code>~</code></td><td>⌥ Option key</td></tr><tr><td><code>$</code></td><td>⇧ Shift key</td></tr><tr><td><code>^</code></td><td>^ Control key</td></tr><tr><td><code>@</code></td><td>⌘ Command key</td></tr><tr><td><code>#</code></td><td>keys on number pad</td></tr></tbody></table><p>参考：</p><ul><li><a href="http://xahlee.info/kbd/osx_keybinding.html">http://xahlee.info/kbd/osx_keybinding.html</a></li><li><a href="https://blog.victormendonca.com/2020/04/27/how-to-change-macos-key-bindings/">https://blog.victormendonca.com/2020/04/27/how-to-change-macos-key-bindings/</a></li></ul><h2 id="关闭启动声音"><a href="#关闭启动声音" class="headerlink" title="关闭启动声音"></a>关闭启动声音</h2><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 关闭提示音</span></span><br><span class="line">sudo nvram <span class="attribute">StartupMute</span>=%01</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打开duang</span></span><br><span class="line">sudo nvram <span class="attribute">StartupMute</span>=%00</span><br></pre></td></tr></table></figure><h2 id="crontab-文件路径"><a href="#crontab-文件路径" class="headerlink" title="crontab 文件路径"></a>crontab 文件路径</h2><p>用户执行<code>crontab -e</code>之后定时任务存储位置<code>/private/var/at/tabs/&lt;username&gt;</code></p><h2 id="hostname-更改"><a href="#hostname-更改" class="headerlink" title="hostname 更改"></a>hostname 更改</h2><p>macos 默认没有设置hostname，导致终端<code>PS1</code>显示时会用网卡的mac地址作为主机名，看起来比较别扭。</p><p>可以通过<code>sudo scutil --set HostName &lt;name&gt;</code>设置想要的主机名</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">➜  ~ hostname</span><br><span class="line">a8a159010000</span><br><span class="line">➜  ~ scutil --<span class="built_in">get</span> HostName</span><br><span class="line">HostName: <span class="keyword">not</span> <span class="built_in">set</span></span><br><span class="line">➜  ~ sudo scutil --<span class="built_in">set</span> HostName Mac-mini</span><br><span class="line">Password:</span><br><span class="line">➜  ~ hostname</span><br><span class="line">ruandeMac-mini</span><br></pre></td></tr></table></figure><h2 id="catalina-禁用系统更新提示"><a href="#catalina-禁用系统更新提示" class="headerlink" title="catalina 禁用系统更新提示"></a>catalina 禁用系统更新提示</h2><p>在终端中输入</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">defaults</span> write com.apple.systempreferences AttentionPrefBundleIDs <span class="number">0</span> ; <span class="attribute">killall</span>  Dock</span><br></pre></td></tr></table></figure><p>在 &#x2F;etc&#x2F;hosts 中加入</p><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># disable mac update</span><br><span class="line"><span class="number">0.0.0.0</span> swdist.apple.com.edgekey.net</span><br><span class="line"><span class="number">0.0.0.0</span> swdist.apple.com.akadns.net</span><br></pre></td></tr></table></figure><h2 id="固定dock位置，防止在不同屏幕间移动"><a href="#固定dock位置，防止在不同屏幕间移动" class="headerlink" title="固定dock位置，防止在不同屏幕间移动"></a>固定dock位置，防止在不同屏幕间移动</h2><p>不是100%有效</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">defaults write com.apple.Dock position-immutable -<span class="type">bool</span> yes; killall Dock</span><br></pre></td></tr></table></figure><h2 id="不生成-开头的隐藏文件"><a href="#不生成-开头的隐藏文件" class="headerlink" title="不生成 ._ 开头的隐藏文件"></a>不生成 <code>._</code> 开头的隐藏文件</h2><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">defaults write com.apple.desktopservices DSDontWriteNetworkStores -<span class="type">bool</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;对程序员来说，macOS 就是一个桌面支持比较好的 Linux&amp;#x2F;Unix，给日常开发带来了许多便利&lt;/p&gt;
&lt;p&gt;本文记录一些日常使用中的小技巧。&lt;/p&gt;</summary>
    
    
    
    <category term="工作生活" scheme="https://blog.ponder.work/categories/life/"/>
    
    
    <category term="mac" scheme="https://blog.ponder.work/tags/mac/"/>
    
  </entry>
  
</feed>
