<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Fish on Binac</title><link>https://binac.org/tags/fish/</link><description>Recent content in Fish on Binac</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><copyright>© 2017-2026 Binac.</copyright><lastBuildDate>Wed, 13 May 2026 19:34:47 +0800</lastBuildDate><atom:link href="https://binac.org/tags/fish/index.xml" rel="self" type="application/rss+xml"/><item><title>fish 的一些问题</title><link>https://binac.org/posts/some-issues-with-fish/</link><pubDate>Wed, 13 May 2026 19:34:47 +0800</pubDate><guid>https://binac.org/posts/some-issues-with-fish/</guid><description>&lt;p&gt;fish 号称是友好的交互式 Shell。它以开箱即用著称，上手的第一时间，原生的补全、高亮、提示令人惊喜，立即体验到 zsh
安装插件才具备的功能，尽管它不兼容 POSIX，但语法简单，性能不如 zsh 的问题也可以忽略，因此我几乎只花了十分钟就决定转向 fish 了。&lt;/p&gt;
&lt;p&gt;然而使用几天后发现，它有一些令人难以忍受的问题。&lt;/p&gt;
&lt;h2 id="奇葩的管道缓冲"&gt;奇葩的管道缓冲&lt;/h2&gt;
&lt;p&gt;先看这行脚本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;bash&lt;/span&gt; &lt;span class="na"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;for i in 1 2 3; do echo $i; sleep 1; done&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;string &lt;/span&gt;match &lt;span class="na"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\d&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;用 bash 模拟一个耗时命令（当然用 fish 也可以），每隔 1 秒输出一个数字，然后用内置的 string 查找。&lt;/p&gt;
&lt;p&gt;原生的 Perl 正则，真是棒极了。没有阻塞，实时输出，一切都很美好。&lt;/p&gt;
&lt;p&gt;那么这样呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;bash&lt;/span&gt; &lt;span class="na"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;for i in 1 2 3; do echo $i; sleep 1; done&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;string &lt;/span&gt;match &lt;span class="na"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\d&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;string &lt;/span&gt;match &lt;span class="na"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\d&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;仅仅多加了一个 string，阻塞发生了：3 秒时没有任何输出，3 秒后才瞬间输出 3 个数字。&lt;/p&gt;
&lt;p&gt;原因是，内置（builtin）、函数（function）均不支持并行&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;。第一个 string 等前面命令的结束才会刷新缓冲，然后一下子输出给第二个
string 使用。所以，管道要实现并行，只能出现最多一个内置或函数。&lt;/p&gt;
&lt;p&gt;等等，这个问题看起来也不严重，我用 grep 不就行了吗？&lt;/p&gt;
&lt;p&gt;好，假设我们这样二次过滤日志：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;tail&lt;/span&gt; &lt;span class="na"&gt;-f&lt;/span&gt; /path/to/log &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;grep&lt;/span&gt; ... &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;grep&lt;/span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;好了，tail 永不关闭，现在什么输出也没有，因为 fish 默认把 grep 包装成了函数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;type &lt;/span&gt;grep
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;grep&lt;/span&gt; is a function &lt;span class="nf"&gt;with definition&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Defined in embedded:functions/grep.fish @ line 7
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;grep&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;command &lt;/span&gt;grep &lt;span class="na"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auto &lt;span class="nv"&gt;$argv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;不巧的是，我甚至把 tail 也包装成函数，让它在包含 -f/-F 选项并且输出端为 tty 时使用 bat 美化输出，于是，我连一次过滤也不能用了，除非使用
&lt;code&gt;command&lt;/code&gt; 强制调用外部命令。&lt;/p&gt;
&lt;p&gt;这还没完，fish 中的别名（alias），例如&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bat -p&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;type &lt;/span&gt;cat
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;cat&lt;/span&gt; is a function &lt;span class="nf"&gt;with definition&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Defined via `source`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cat&lt;/span&gt; &lt;span class="na"&gt;--wraps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bat -p&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;--description&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;alias cat=bat -p&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;bat&lt;/span&gt; &lt;span class="na"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$argv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;本质上也是函数，因此也会阻塞。如果你想删除 alias，fish 并不支持类似 &lt;code&gt;unalias&lt;/code&gt; 的功能，你需要使用 &lt;code&gt;functions -e&lt;/code&gt;
删除函数。这也是 fish 总是推荐用 abbr 的原因：alias 有时并不好用。&lt;/p&gt;
&lt;p&gt;这个问题从 2014 到 2026 都没有解决。友好的交互式 Shell，并没有看上去那么美好。&lt;/p&gt;
&lt;h2 id="缺失的后台"&gt;缺失的后台&lt;/h2&gt;
&lt;p&gt;这与管道缓冲的问题是类似的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f; sleep 1; echo done; end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;f &amp;amp;&lt;/code&gt; 并不能让函数在后台运行&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;，fish 也不支持 subshell。内置命令、代码块也是如此。从 2012 到 2026 都没有解决。&lt;/p&gt;
&lt;p&gt;而在 bash/zsh 甚至 dash 中&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;f&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; sleep 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;f &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;都没有问题，没记错的话这应该是 POSIX 标准。&lt;/p&gt;
&lt;p&gt;设想对 curl 做包装，添加了环境变量或者 &lt;code&gt;--retry&lt;/code&gt; 参数之类的，然后使用 &lt;code&gt;curl -o ... &amp;amp;&lt;/code&gt;
试图丢到后台下载大文件，它却强制停在前台。&lt;/p&gt;
&lt;p&gt;这比管道缓冲的问题还奇葩，对管道只要 fish 本身的进程（如函数）不超过一个，或者强制使用外部命令即可，但要想后台运行函数，那就只能在 fish 中使用
&lt;code&gt;fish -c '...'&lt;/code&gt; 这种奇葩操作了。&lt;/p&gt;
&lt;p&gt;官方文档（截至目前是 4.7.1）的相关解释：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fish does not currently have subshells.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不支持 subshell。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Builtin&lt;/strong&gt;: A command that is implemented by the shell. Builtins are so
closely tied to the operation of the shell that it is impossible to implement
them as external commands. In &lt;code&gt;echo foo&lt;/code&gt;, the “echo” is a builtin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Command&lt;/strong&gt;: A program that the shell can run, or more specifically an
external program that the shell runs in another process. External commands are
provided on your system, as executable files. In &lt;code&gt;echo foo&lt;/code&gt; the “echo” is a
builtin command, in &lt;code&gt;command echo foo&lt;/code&gt; the “echo” is an external command,
provided by a file like /bin/echo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Function&lt;/strong&gt;: A block of commands that can be called as if they were a single
command. By using functions, it is possible to string together multiple simple
commands into one more advanced command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Job&lt;/strong&gt;: A running pipeline or command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pipeline&lt;/strong&gt;: A set of commands strung together so that the output of one
command is the input of the next command. &lt;code&gt;echo foo | grep foo&lt;/code&gt; is a
pipeline.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不知道把这一坨术语丢给爱因斯坦，他能不能理解 builtin 到底是不是 command，包含 builtin/function
的一连串“commands”到底算不算 pipeline。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;题外话，在稍微冷门或专业一点的领域，AI 的表现都是一本正经地胡说八道。&lt;/p&gt;
&lt;p&gt;ChatGPT 总体比 DeepSeek 准确率高。但它很固执，即使我将手动测试结果、官方文档和 GitHub issue 链接丢给它，它也在找补。&lt;/p&gt;
&lt;p&gt;而 DeepSeek 则总是讨好用户，说什么都是一通夸，据说其他国产 AI 也是如此。&lt;/p&gt;
&lt;p&gt;没想到居然从 AI 上看到了民族性。&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://github.com/fish-shell/fish-shell/issues/1396"target="_blank" rel="noopener noreferrer"&gt;https://github.com/fish-shell/fish-shell/issues/1396&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://github.com/fish-shell/fish-shell/issues/238"target="_blank" rel="noopener noreferrer"&gt;https://github.com/fish-shell/fish-shell/issues/238&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://fishshell.com/docs/current/fish_for_bash_users.html#subshells"target="_blank" rel="noopener noreferrer"&gt;https://fishshell.com/docs/current/fish_for_bash_users.html#subshells&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a href="https://fishshell.com/docs/current/language.html#terminology"target="_blank" rel="noopener noreferrer"&gt;https://fishshell.com/docs/current/language.html#terminology&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>板绘学习</title><link>https://binac.org/tuya/learning-banhui/</link><pubDate>Fri, 03 Jan 2025 02:20:05 +0800</pubDate><guid>https://binac.org/tuya/learning-banhui/</guid><description>&lt;h2 id="基本功"&gt;基本功&lt;/h2&gt;
&lt;h3 id="黑白灰度"&gt;黑白（灰度）&lt;/h3&gt;
&lt;p&gt;色相/饱和度，将饱和度调整到最低得到灰阶图。&lt;/p&gt;
&lt;p&gt;对于拾色器上的 HSV（或 HSB），H 表示色相（Hue），S 表示饱和度（Saturation），V 或 B 表示明度（Value 或 Brightness）。&lt;/p&gt;
&lt;p&gt;色相的值是色环上的角度，0 是红色，120 是绿色，240 是蓝色。&lt;/p&gt;
&lt;p&gt;饱和度越高，颜色越深，越接近光谱色；饱和度越低，颜色越灰白。&lt;/p&gt;
&lt;p&gt;明度越小，光谱色中添加黑色越多，颜色越暗黑。&lt;/p&gt;
&lt;p&gt;三大面：黑（背光面）、白（受光面）、灰（侧光面）。&lt;/p&gt;
&lt;p&gt;五大调：亮面（高光）、灰面、明暗交界线、反光、投影。&lt;/p&gt;
&lt;span class="image-container"&gt;&lt;span class="link" &gt;&lt;a href="https://pic4.zhimg.com/v2-7d041548f33edecc3e9e162812d9c7f5_1440w.jpg"
target="_blank"&gt;&lt;img loading="lazy" class="img" src="https://pic4.zhimg.com/v2-7d041548f33edecc3e9e162812d9c7f5_1440w.jpg"/&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;p&gt;关于五大调这个老师讲的不对，上面采用网上主流说法。&lt;/p&gt;
&lt;p&gt;将饱和度调到最低画灰阶图，调整明度得到亮面、暗面等的颜色。&lt;/p&gt;
&lt;h3 id="透视"&gt;透视&lt;/h3&gt;
&lt;p&gt;消失点（灭点）分别有一、二、三个的透视：&lt;/p&gt;
&lt;span class="image-container"&gt;&lt;span class="link" &gt;&lt;a href="%e9%80%8f%e8%a7%86.jpg"
target="_blank"&gt;&lt;img loading="lazy" class="img" src="%e9%80%8f%e8%a7%86.jpg"/&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;p&gt;第三个虽然看起来有四个消失点，但上面那个和下面那个只需要一个就是三点透视。&lt;/p&gt;
&lt;h3 id="构图"&gt;构图&lt;/h3&gt;
&lt;p&gt;使用裁剪工具里的九宫格、金色螺线等检验构图。&lt;/p&gt;
&lt;h3 id="伪厚涂上色"&gt;伪厚涂上色&lt;/h3&gt;
&lt;p&gt;不知道是画的问题还是老师的问题或者是我的问题，教程里的画太丑。不画。&lt;/p&gt;
&lt;h3 id="打型"&gt;打型&lt;/h3&gt;
&lt;p&gt;打型好难，不过勾线比较简单。平涂 19 号勾起来很爽。&lt;/p&gt;
&lt;span class="image-container"&gt;&lt;span class="link" &gt;&lt;a href="%e6%89%93%e5%9e%8b.png"
target="_blank"&gt;&lt;img loading="lazy" class="img" src="%e6%89%93%e5%9e%8b.png"/&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;h3 id="项圈"&gt;项圈&lt;/h3&gt;
&lt;p&gt;橡皮工具用错了，要用平头我给用成了色块平涂，线条深一块浅一块。&lt;/p&gt;
&lt;span class="image-container"&gt;&lt;span class="link" &gt;&lt;a href="%e9%a1%b9%e5%9c%88.png"
target="_blank"&gt;&lt;img loading="lazy" class="img" src="%e9%a1%b9%e5%9c%88.png"/&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;p&gt;勾线时，在外轮廓、结构交接、凹陷、不受光处使用粗线条，在细节、凸出、受光、虚、远处使用细线条。&lt;/p&gt;
&lt;p&gt;嗯，完美避开了正确答案。&lt;/p&gt;
&lt;h3 id="武器线稿"&gt;武器线稿&lt;/h3&gt;
&lt;p&gt;手抖得像得了帕金森。&lt;/p&gt;
&lt;p&gt;线条很丑，画完才发现笔刷用错了，用了打草稿的笔刷勾线。&lt;/p&gt;
&lt;p&gt;要多用撤销和橡皮擦。画笔大小打草稿时粗一点，勾线时细一点。&lt;/p&gt;
&lt;span class="image-container"&gt;&lt;span class="link" &gt;&lt;a href="%e6%ad%a6%e5%99%a8.png"
target="_blank"&gt;&lt;img loading="lazy" class="img" src="%e6%ad%a6%e5%99%a8.png"/&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/span&gt;</description></item><item><title>为邮件客户端添加 OAuth 2.0 代理</title><link>https://binac.org/posts/oauth-2.0-proxy-for-email-client/</link><pubDate>Tue, 15 Oct 2024 02:20:59 +0800</pubDate><guid>https://binac.org/posts/oauth-2.0-proxy-for-email-client/</guid><description>&lt;p&gt;强制使用 OAuth 2.0 认证的邮件提供商越来越多，而一些老客户端并不支持，比如 macOS
Catalina 自带的邮件。&lt;/p&gt;
&lt;p&gt;于是尝试换 Thunderbird，然而用起来有些……粗犷。&lt;/p&gt;
&lt;p&gt;找到一个开源程序
&lt;a href="https://github.com/simonrob/email-oauth2-proxy"target="_blank" rel="noopener noreferrer"&gt;email-oauth2-proxy&lt;/a&gt;，它可以建立一个代理，让不支持 OAuth
2.0 的客户端使用 IMAP/POP/SMTP 协议继续访问邮箱，但它的文档写得实在不像给人类看的。&lt;/p&gt;
&lt;p&gt;下面以 macOS Catalina 自带邮件客户端添加 Outlook 个人邮箱为例进行说明。&lt;/p&gt;
&lt;h2 id="安装"&gt;安装&lt;/h2&gt;
&lt;p&gt;我不需要 GUI，因此直接安装：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pipx install emailproxy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="配置"&gt;配置&lt;/h2&gt;
&lt;p&gt;建立配置文件 &lt;code&gt;~/.config/emailproxy.conf&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[IMAP-1993]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;server_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;outlook.office365.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;server_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;993&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;local_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[SMTP-1587]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;server_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;smtp-mail.outlook.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;server_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;587&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;server_starttls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;local_address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;127.0.0.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[your.address@example.com]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;permission_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://login.microsoftonline.com/common/oauth2/v2.0/authorize&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;token_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://login.microsoftonline.com/common/oauth2/v2.0/token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;oauth2_scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send offline_access&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;redirect_uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;http://localhost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;9e5f94bc-e8a4-4e73-b8be-63364c29d753&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;由程序与邮件服务器建立连接，客户端只需以 IMAP/SMTP 等协议访问本地代理，访问本地是不加密的，一般也没必要。&lt;/p&gt;
&lt;p&gt;本地端口不要用默认端口（如 993/587），因为默认端口不仅需要以 root 用户运行，macOS 的邮件客户端也不能添加，否则会提示“服务器拒绝允许通过默认端口进行的连接”。这里用的是 1993/1587。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;smtp-mail.outlook.com&lt;/code&gt; 是微软用于个人免费账号的 SMTP 服务器，Office
365 账号有自己的服务器（具体自行查找相关文档），但也可以使用免费服务器。微软 SMTP 服务启用了 STARTTLS，因此
&lt;code&gt;server_starttls&lt;/code&gt; 设置为 &lt;code&gt;True&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;your.address@example.com&lt;/code&gt; 改为自己的邮箱地址。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;client_id&lt;/code&gt; 用于模拟某个邮件客户端，这里的 ID 来自
&lt;a href="https://github.com/mozilla/releases-comm-central/blob/master/mailnews/base/src/OAuth2Providers.sys.mjs"target="_blank" rel="noopener noreferrer"&gt;Thunderbird&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="授权"&gt;授权&lt;/h2&gt;
&lt;p&gt;首次使用必须先登录授权。运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;emailproxy --no-gui --config-file ~/.config/emailproxy.conf --external-auth
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;典型的流程是：程序将会等待客户端登录，当收到请求时打印一个链接，用浏览器打开并授权，自动跳转到
&lt;code&gt;redirect_uri&lt;/code&gt;
开头的链接，回到终端输入跳转后的链接，授权成功。token 默认保存在配置文件中。&lt;/p&gt;
&lt;p&gt;由于苹果自带邮箱太简陋，登录时遇到很多麻烦。&lt;/p&gt;
&lt;p&gt;首先添加账号时，邮件地址不能写真实地址，否则客户端检测到是微软的域名，就自作聪明建立连接，但这显然不可能成功。可以先写无法解析的地址，例如
&lt;code&gt;a@b&lt;/code&gt;，之后进入下一步再修改。&lt;/p&gt;
&lt;p&gt;密码可以按自己喜好填写，因为它不是与邮件提供商通信的密码，只是本地代理的密码，程序会自动记住。但再次登录时要保持一致。&lt;/p&gt;
&lt;p&gt;发件和收件服务器都填写
&lt;code&gt;localhost&lt;/code&gt;。提示错误可以忽略。总之先建立一个账号，才能进入偏好设置进行详细设置。苹果有点过于把用户当傻子了。&lt;/p&gt;
&lt;p&gt;在偏好设置中找到添加的账号，点击右侧服务器设置，用户名和密码自行设置，取消勾选“自动管理连接设置”，端口改为 1993，在“高级 IMAP 设置”里勾选“允许不安全认证”。&lt;/p&gt;
&lt;p&gt;下方类似地编辑 SMTP 设置，同样将端口改为 1587，并允许不安全认证。&lt;/p&gt;
&lt;p&gt;这时终端会输出需要授权的链接，类似于：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ emailproxy --no-gui --config-file ~/.config/emailproxy.conf --external-auth
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Initialising Email OAuth 2.0 Proxy &lt;span class="o"&gt;(&lt;/span&gt;version 2024-09-12&lt;span class="o"&gt;)&lt;/span&gt; from config file /path/to/.config/emailproxy.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Starting IMAP server at 127.0.0.1:1993 &lt;span class="o"&gt;(&lt;/span&gt;unsecured&lt;span class="o"&gt;)&lt;/span&gt; proxying outlook.office365.com:993 &lt;span class="o"&gt;(&lt;/span&gt;SSL/TLS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Starting SMTP server at 127.0.0.1:1587 &lt;span class="o"&gt;(&lt;/span&gt;unsecured&lt;span class="o"&gt;)&lt;/span&gt; proxying smtp-mail.outlook.com:587 &lt;span class="o"&gt;(&lt;/span&gt;STARTTLS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Initialised Email OAuth 2.0 Proxy - listening &lt;span class="k"&gt;for&lt;/span&gt; authentication requests. Connect your email client to begin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Accepting new connection from &lt;span class="o"&gt;[&lt;/span&gt;::ffff:127.0.0.1&lt;span class="o"&gt;]&lt;/span&gt;:52602 to IMAP server at 127.0.0.1:1993 &lt;span class="o"&gt;(&lt;/span&gt;unsecured&lt;span class="o"&gt;)&lt;/span&gt; proxying outlook.office365.com:993 &lt;span class="o"&gt;(&lt;/span&gt;SSL/TLS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Authorisation request received &lt;span class="k"&gt;for&lt;/span&gt; your.address@example.com &lt;span class="o"&gt;(&lt;/span&gt;external auth mode&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Email OAuth 2.0 Proxy No-GUI external auth mode: please authorise a request &lt;span class="k"&gt;for&lt;/span&gt; account your.address@example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2024-10-15 00:00:00: Please visit the following URL to authenticate account your.address@example.com: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id&lt;span class="o"&gt;=&lt;/span&gt;9e5f94bc-e8a4-4e73-b8be-63364c29d753&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;redirect_uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http%3A%2F%2Flocalhost&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https%3A%2F%2Foutlook.office.com%2FIMAP.AccessAsUser.All%20https%3A%2F%2Foutlook.office.com%2FPOP.AccessAsUser.All%20https%3A%2F%2Foutlook.office.com%2FSMTP.Send%20offline_access&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;response_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;code&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;access_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;offline&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;login_hint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your.address%40example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Copy+paste or press &lt;span class="o"&gt;[&lt;/span&gt;↵ Return&lt;span class="o"&gt;]&lt;/span&gt; to visit the following URL and authenticate account your.address@example.com: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_i
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9e5f94bc-e8a4-4e73-b8be-63364c29d753&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;redirect_uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http%3A%2F%2Flocalhost&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https%3A%2F%2Foutlook.office.com%2FIMAP.AccessAsUser.All%20https%3A%2F%2Foutlook.office.com%2FPO
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;P.AccessAsUser.All%20https%3A%2F%2Foutlook.office.com%2FSMTP.Send%20offline_access&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;response_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;code&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;access_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;offline&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;login_hint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your.address%40example.com &lt;span class="k"&gt;then&lt;/span&gt; paste here the
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;full post-authentication URL from the browser&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;s address bar &lt;span class="o"&gt;(&lt;/span&gt;it should start with http://localhost&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;打开此链接，授权后自动跳转到类似
&lt;code&gt;http://localhost/?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;/code&gt;
的地址，网页本身是否显示成功并不重要，只要将此地址复制到终端回车即可授权完成。&lt;/p&gt;
&lt;p&gt;至此邮箱登录成功。以后只需要运行
&lt;code&gt;emailproxy --no-gui --config-file ~/.config/emailproxy.conf&lt;/code&gt; 即可。&lt;/p&gt;
&lt;p&gt;另外需要注意的是，账号的“邮箱行为”中，发件箱建议改为“已发送邮件”，否则发送邮件后，服务器发件箱将存在两份一模一样的邮件。但这样改后，本地发件箱将不会与服务器完全同步，例如删除本地发件箱中的邮件，服务器中仍然存在。&lt;/p&gt;
&lt;h2 id="自动启动"&gt;自动启动&lt;/h2&gt;
&lt;p&gt;新建 &lt;code&gt;~/Library/LaunchAgents/org.binac.emailproxy.plist&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;org.binac.emailproxy&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/bin/emailproxy&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;--no-gui&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;--config-file&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/path/to/.config/emailproxy.conf&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;--log-file&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/local/var/log/emailproxy.log&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;KeepAlive&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;修改其中配置文件的路径，并加载：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;launchctl load ~/Library/LaunchAgents/org.binac.emailproxy.plist
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>下载官方旧版本 Chrome 并禁止更新</title><link>https://binac.org/posts/downloading-older-versions-of-chrome-and-disabling-updates/</link><pubDate>Tue, 30 Jul 2024 01:17:22 +0800</pubDate><guid>https://binac.org/posts/downloading-older-versions-of-chrome-and-disabling-updates/</guid><description>&lt;p&gt;已经忍 Chrome 很久了，但是如果换其他浏览器，想想那一堆扩展脚本密码书签就头疼。整天提醒更新，不知道更新了个什么玩意，反而把原来的功能改成依托答辩。&lt;/p&gt;
&lt;p&gt;万万没想到，在三哥的英明领导下，狗鸽又拉了几坨大的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想要复制下载链接只能到下载记录里。&lt;/li&gt;
&lt;li&gt;升级到 127.0.6533.73，下载记录没有链接了，光标移动到上面都不行，只能看到域名。&lt;/li&gt;
&lt;li&gt;升级到 128.0.6613.138，这下连域名都看不到了，只有在下载完成后光标移到文件名上有链接。&lt;/li&gt;
&lt;li&gt;129 以后，Catalina 也不支持了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;地铁老人手机.jpg&lt;/p&gt;
&lt;p&gt;就有没有一种可能，我只想获取文件的真实链接呢？这就是地球上最流行的浏览器吗？张小龙都觉得抽象。&lt;/p&gt;
&lt;p&gt;于是去找历史版本，结果狗鸽居然不提供，只给过时系统提供最后一个版本。&lt;/p&gt;
&lt;p&gt;去第三方网站 uptodown.com 下载最新版，和狗鸽官网的 sha256 都对不上，这尼玛谁敢用？&lt;/p&gt;
&lt;p&gt;也翻了很多号称官方下载的帖子，这些工具号称是 Chrome，其实都是 Chromium，我也是服了。&lt;/p&gt;
&lt;p&gt;最后终于找到了一个获取 Chrome 官方链接的 Python 脚本&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;，不过只能获取 macOS 版 Chrome，用 Shell 重写了一下。&lt;/p&gt;
&lt;p&gt;还找到一个 Windows 适用的&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;，但是没试过。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;2025 年 9 月 6 日更新：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;沟槽的狗哥，go1.25 害我不得不升级 macOS。&lt;/p&gt;
&lt;p&gt;但是我明明已经禁止了更新程序，它在后台不知道怎么就偷偷把 Chrome 升级到 140 了。而且就算装回旧版本，一重启就变成新版。&lt;/p&gt;
&lt;p&gt;关键是它给强制升级到 Manifest V3 了。&lt;/p&gt;
&lt;p&gt;这次在文末更新一下&lt;a href="#%E7%A6%81%E6%AD%A2%E6%9B%B4%E6%96%B0"&gt;禁止更新&lt;/a&gt;的方法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="shell-脚本"&gt;Shell 脚本&lt;/h2&gt;
&lt;figure style="margin:0 0 1.5em 0"&gt;
&lt;figcaption style="text-align: right"&gt;
&lt;a href="https://binac.org/posts/downloading-older-versions-of-chrome-and-disabling-updates/get-chrome.sh" style="color: lightslategrey;font-size: 0.8em"&gt;get-chrome.sh&lt;/a&gt;
&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;span class="lnt"&gt;72
&lt;/span&gt;&lt;span class="lnt"&gt;73
&lt;/span&gt;&lt;span class="lnt"&gt;74
&lt;/span&gt;&lt;span class="lnt"&gt;75
&lt;/span&gt;&lt;span class="lnt"&gt;76
&lt;/span&gt;&lt;span class="lnt"&gt;77
&lt;/span&gt;&lt;span class="lnt"&gt;78
&lt;/span&gt;&lt;span class="lnt"&gt;79
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -e
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;uuidgen &lt;span class="p"&gt;|&lt;/span&gt; tr &lt;span class="s1"&gt;&amp;#39;[:upper:]&amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[:lower:]&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mac
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;OS_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TARGET_VERSION_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;uname&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; Darwin &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;OS_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;sw_vers -productVersion &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s1"&gt;&amp;#39;[[:digit:]]\+\.[[:digit:]]\+&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PRINT_ALL_LINKS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PRINT_SHA256&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SHA_256&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;USAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cat &lt;span class="s"&gt;&amp;lt;&amp;lt;-END
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Usage: $SCRIPT [&amp;lt;options&amp;gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Get official download link of Chrome for macOS.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -o &amp;lt;os_version&amp;gt; set OS version, e.g. &amp;#39;10.15&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -t &amp;lt;target_version_prefix&amp;gt; set target version prefix of Chrome, e.g. &amp;#39;126&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -a print all available links (by default, print first HTTPS link only)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -s print sha256 checksum
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -h display this help and exit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Home page: &amp;lt;https://binac.org/posts/download-an-old-version-of-chrome/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;END&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;_exit&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;asho:t:&amp;#34;&lt;/span&gt; c&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; o&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;OS_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; t&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;TARGET_VERSION_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; a&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;PRINT_ALL_LINKS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;PRINT_SHA256&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; h&lt;span class="o"&gt;)&lt;/span&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *&lt;span class="o"&gt;)&lt;/span&gt; _exit &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OS_VERSION&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; error &lt;span class="s2"&gt;&amp;#34;OS version not set&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://tools.google.com/service/update2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DATA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;request protocol=\&amp;#34;3.0\&amp;#34; requestid=\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$UUID&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;lt;os platform=\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PLATFORM&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34; version=\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OS_VERSION&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;lt;app appid=\&amp;#34;com.google.Chrome\&amp;#34; lang=\&amp;#34;en-us\&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;lt;updatecheck targetversionprefix=\&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TARGET_VERSION_PREFIX&lt;/span&gt;&lt;span class="s2"&gt;\&amp;#34;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;lt;/app&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;lt;/request&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CONTENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -fsSL -X POST &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -d &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DATA&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;LINKS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CONTENT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s1"&gt;&amp;#39;https\?://[^&amp;#34;]*&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CONTENT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s1"&gt;&amp;#39; name=&amp;#34;[^&amp;#34;]*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cut -c 8-&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PRINT_ALL_LINKS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;LINKS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$LINKS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s1"&gt;&amp;#39;https://[^&amp;#34;]*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -1&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; link in &lt;span class="nv"&gt;$LINKS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$link$NAME&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SHA_256&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CONTENT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s1"&gt;&amp;#39;hash_sha256=&amp;#34;[^&amp;#34;]*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cut -c 14-&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PRINT_SHA256&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;sha256 &lt;/span&gt;&lt;span class="nv"&gt;$SHA_256&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;默认输出第一个 HTTPS 链接，用 &lt;code&gt;-o&lt;/code&gt; 指定系统版本（需要注意，见后文），&lt;code&gt;-t&lt;/code&gt; 指定大版本，&lt;code&gt;-a&lt;/code&gt; 打印所有链接，&lt;code&gt;-s&lt;/code&gt; 打印
sha256，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ get-chrome.sh -o &lt;span class="m"&gt;11&lt;/span&gt; -t &lt;span class="m"&gt;126&lt;/span&gt; -s
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/c27alpqp6yqlnckxwaxrg35r2y_126.0.6478.183/GoogleChrome-126.0.6478.183.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 7bb3660120eb9e857f371b390f470a530ef2e956e6426a14b4193751684393f6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;脚本是从 tools.google.com 获取，得到链接域名是 gvt1.com，这应该也是狗鸽的域名。&lt;/p&gt;
&lt;p&gt;狗鸽最贱的一点是，对废弃的系统，它只会返回最后支持的版本。&lt;/p&gt;
&lt;p&gt;比如对 Mojave（10.14），它就只返回 116.0.5845.187。对 Catalina（10.15），它就只返回 128.0.6613.138，即使
128 以前的版本也可用。&lt;/p&gt;
&lt;p&gt;只有用 &lt;code&gt;-o 11&lt;/code&gt; 指定系统为 Big Sur（11）时，它才会老实，但也只能得到支持 Big Sur 以后的版本（&amp;gt;=122），目前版本如下（均可用于
Catalina）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="k"&gt;for&lt;/span&gt; v in &lt;span class="k"&gt;$(&lt;/span&gt;seq &lt;span class="m"&gt;100&lt;/span&gt; 128&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; get-chrome.sh -o &lt;span class="m"&gt;11&lt;/span&gt; -t &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$v&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -s&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/mem3daavyalw3ej62rqnfxrt4e_122.0.6261.129/GoogleChrome-122.0.6261.129.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 655542b6a1cf41d4982a2c1353310855dfa03834d18253f54910cca0289fa6a1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/ad5yyrouti3mrydk3xx37nwvpiga_123.0.6312.124/GoogleChrome-123.0.6312.124.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 8393cf83853624488e1ac31633c2787686b0c5b8ddec5d86ce96611ad29f3237
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/pyibrq4fvbzbiecijliwb7oyxa_124.0.6367.208/GoogleChrome-124.0.6367.208.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 698b37c47639941443616cb629b2a1f13f45b0a856848d97905ebb5440d06c09
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/i5incd454szmu3rscl5w6r5qga_125.0.6422.142/GoogleChrome-125.0.6422.142.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 0a2f19d0a00c256dc6ed005abdcda43c7c1a91fc5dadfb781e33972be8be91dc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/c27alpqp6yqlnckxwaxrg35r2y_126.0.6478.183/GoogleChrome-126.0.6478.183.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 7bb3660120eb9e857f371b390f470a530ef2e956e6426a14b4193751684393f6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/adqui4t7hzlljw2m2mmu2dvb6tmq_127.0.6533.120/GoogleChrome-127.0.6533.120.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 91f5024eef012067194cc69bcb48b294e085c48fa92d772af5f2a8da8c3a1eb3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/gtm24cqmnwgcp7dtscvlmsbrwa_128.0.6613.138/GoogleChrome-128.0.6613.138.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 c1dea7dc078ac4a773fb95fd5f14e2d501f3981cb329180859c7378811e1b59b
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我对这些版本都做了备份，虽然大概率没什么用。需要的可以联系我。&lt;/p&gt;
&lt;p&gt;对 10.* 所有版本运行结果如下（这些下载链接在官网的“其他平台”可以找到）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="k"&gt;for&lt;/span&gt; o in &lt;span class="k"&gt;$(&lt;/span&gt;seq &lt;span class="m"&gt;0&lt;/span&gt; 15&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; get-chrome.sh -o &lt;span class="s2"&gt;&amp;#34;10.&lt;/span&gt;&lt;span class="nv"&gt;$o&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -s &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;^^^ 10.&lt;/span&gt;&lt;span class="nv"&gt;$o&lt;/span&gt;&lt;span class="s2"&gt; ^^^&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://dl.google.com/release2/b7c5syhum0krv8iwaxqj75m937t1mxerm4vt7t39qyqq7foxhnpagw0908czwsgdwz9v1phqybtkyzal6afib1gu2x59e3r0px1/GoogleChrome-49.0.2623.112-Beta.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 b8fb5e4795ff5d4e7501f88b432da558fd54cd3afe078e1cfcd37a917ae5b711
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.6 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://dl.google.com/release2/b7c5syhum0krv8iwaxqj75m937t1mxerm4vt7t39qyqq7foxhnpagw0908czwsgdwz9v1phqybtkyzal6afib1gu2x59e3r0px1/GoogleChrome-49.0.2623.112-Beta.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 b8fb5e4795ff5d4e7501f88b432da558fd54cd3afe078e1cfcd37a917ae5b711
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.7 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/fOFsZ-5dHaI_65.0.3325.181/GoogleChrome-65.0.3325.181.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 c66f934750643c18e09f09f3103e235d3b6d48dda44dac9993ca53cf95db0d2d
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.9 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/fnc67wptv5dybsdbrounkk36mu_103.0.5060.134/GoogleChrome-103.0.5060.134.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 cec46f072974f5eb82f660328d395cde5c3e6b2056e5ccba628a7af1c722123a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.11 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/fnc67wptv5dybsdbrounkk36mu_103.0.5060.134/GoogleChrome-103.0.5060.134.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 cec46f072974f5eb82f660328d395cde5c3e6b2056e5ccba628a7af1c722123a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.12 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/acmm55cy3sg73rcub4rfcdfjjmza_116.0.5845.187/GoogleChrome-116.0.5845.187.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 9c338299144c8eabe355e2cfb740dfd1f3d8fe42732f2e015e9901c0b9c499aa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.13 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/acmm55cy3sg73rcub4rfcdfjjmza_116.0.5845.187/GoogleChrome-116.0.5845.187.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 9c338299144c8eabe355e2cfb740dfd1f3d8fe42732f2e015e9901c0b9c499aa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.14 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://redirector.gvt1.com/edgedl/release2/chrome/gtm24cqmnwgcp7dtscvlmsbrwa_128.0.6613.138/GoogleChrome-128.0.6613.138.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sha256 c1dea7dc078ac4a773fb95fd5f14e2d501f3981cb329180859c7378811e1b59b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^ 10.15 ^^^
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="python-脚本"&gt;Python 脚本&lt;/h2&gt;
&lt;script src="https://gist.github.com/ryfloyd/afc8d87947a520e6094fec4878051ea8.js?file=google_chrome_update_checker.py"&gt;&lt;/script&gt;
&lt;p&gt;把&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;os_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;14.2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;target_version_prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;114&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;改一下就能用了。&lt;/p&gt;
&lt;h2 id="禁止更新"&gt;禁止更新&lt;/h2&gt;
&lt;p&gt;在更新程序保活这件事上，狗哥的不要脸程度堪比 360。&lt;/p&gt;
&lt;p&gt;首先把 &lt;code&gt;tools.google.com&lt;/code&gt; 和 &lt;code&gt;update.googleapis.com&lt;/code&gt; 这俩域名屏蔽掉，例如在 hosts 文件里指向回环地址。&lt;/p&gt;
&lt;p&gt;如果使用梯子，可以写在 block 路由规则里，放到 proxy 前。&lt;/p&gt;
&lt;p&gt;然后把更新程序干掉：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo rm -rf /Library/Google/GoogleSoftwareUpdate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf /Applications/Google&lt;span class="se"&gt;\ &lt;/span&gt;Chrome.app/Contents/Frameworks/Google&lt;span class="se"&gt;\ &lt;/span&gt;Chrome&lt;span class="se"&gt;\ &lt;/span&gt;Framework.framework/Versions/Current/Helpers/GoogleUpdater.app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf ~/Library/Google/GoogleSoftwareUpdate/*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chflags uchg ~/Library/Google/GoogleSoftwareUpdate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/Google/GoogleUpdater
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf ~/Library/HTTPStorages/com.google.GoogleUpdater
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf ~/Library/Caches/com.google.GoogleUpdater
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm ~/Library/LaunchAgents/com.google.keystone.agent.plist
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm ~/Library/LaunchAgents/com.google.keystone.xpcservice.plist
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;launchctl unload -w ~/Library/LaunchAgents/com.google.GoogleUpdater.wake.plist
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;: &amp;gt;~/Library/LaunchAgents/com.google.GoogleUpdater.wake.plist
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chflags uchg ~/Library/LaunchAgents/com.google.GoogleUpdater.wake.plist
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;defaults write com.google.Keystone.Agent checkInterval &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这些傻逼玩意会在重启后自动生成，必要的时候用 &lt;code&gt;chflags uchg&lt;/code&gt; 禁止写入。&lt;/p&gt;
&lt;p&gt;还有一个脑瘫的玩意，就是“Chrome 版本太旧”。这玩意可以用命令行参数 &lt;code&gt;--disable-background-networking&lt;/code&gt; 解决，或者用
&lt;code&gt;--simulate-outdated-no-au&lt;/code&gt;&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;为此可以用“自动操作”新建一个 Shell 脚本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/Applications/Google&lt;span class="se"&gt;\ &lt;/span&gt;Chrome.app/Contents/MacOS/Google&lt;span class="se"&gt;\ &lt;/span&gt;Chrome --disable-background-networking
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;另存为 Chrome.app，需要的时候打开它就行了。但是这样可能需要重新登录账号。&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/77869298"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/77869298&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://squirrelistic.com/blog/how_to_download_older_version_of_google_chrome"target="_blank" rel="noopener noreferrer"&gt;https://squirrelistic.com/blog/how_to_download_older_version_of_google_chrome&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/q/27962454"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/q/27962454&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>《聊斋志异》读后感</title><link>https://binac.org/tuya/liaozhaizhiyi-duhougan/</link><pubDate>Thu, 27 Jun 2024 22:52:15 +0800</pubDate><guid>https://binac.org/tuya/liaozhaizhiyi-duhougan/</guid><description>&lt;h2 id="卷一"&gt;卷一&lt;/h2&gt;
&lt;h3 id="考城隍"&gt;考城隍&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;一个秀才生病卧床，忽然有人请他去考试。秀才发现考场不简单，考官居然是神仙。&lt;/p&gt;
&lt;p&gt;答完题神仙很满意，让秀才去当城隍，秀才这才知道自己阳寿已尽，但老母还需供养，请求给老母送终后再上任，于是神仙让同考的张生代行九年职责。&lt;/p&gt;
&lt;p&gt;秀才本来已经死了三天，遂复活。打听张生的事，果然是当天也死了。九年后母亲去世，秀才沐浴更衣也死了，岳父还见到他来告别。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从这篇故事可以体会古人读书入仕的愿望。活着是廪生（领取公家膳食的秀才），死了考城隍。&lt;/p&gt;
&lt;p&gt;但秀才作答的文章：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有心为善，虽善不赏；无心为恶，虽恶不罚。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;神仙们“传赞不已”，我却认为值得商榷。&lt;/p&gt;
&lt;p&gt;没有人能窥探他人的内心，难以判断有意无意（或许神仙可以，但秀才作答时并不知道考的是神仙编制）。为官者如果不能遵从客观事实，而是窥探他人内心来做判断，真不知道会乱成什么样子。&lt;/p&gt;
&lt;p&gt;对于前一句，作者可能是出于对作秀出名的厌恶写的，例如《二十四孝》中的离谱故事，察举制时期的自我炒作等。&lt;/p&gt;
&lt;p&gt;但是另一方面，论迹不论心，表面文章也很重要。古代有千金买马骨的故事，把诚意表现出来，让人们知道为善有嘉奖，作恶有惩罚，才能正确引领社会风气。&lt;/p&gt;
&lt;p&gt;对于后一句，也不能一概而论。&lt;/p&gt;
&lt;p&gt;假如一行人闯红灯，被正常行驶的车辆撞死，车主是否应该负部分责任？&lt;/p&gt;
&lt;p&gt;如果仅从车主无心之过来分析，不应该。但权利和责任是对等的，车辆在马路上比行人享有更大的权利，而且是强势方，因此承担部分责任是合理的。按照我国法律，这种情况机动车一般会承担不超过百分之十的责任。&lt;/p&gt;
&lt;p&gt;这是全书第一篇故事，不免令人联想到蒲松龄本人的境遇。&lt;/p&gt;
&lt;p&gt;他少年得志，19 岁县、府、道试均夺得第一名，取中秀才，之后却屡屡受挫，46 岁才被补为廪生，72 岁才被补为贡生，76 岁病逝。&lt;/p&gt;
&lt;p&gt;这篇文章或许是作者本人的内心投射。&lt;/p&gt;
&lt;h3 id="耳中人"&gt;耳中人&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;一个秀才练气功，觉得耳朵里有说话声。又一次练功时，应和声音引它出来，竟是一个三寸长的夜叉状小人。这时邻居突然来借东西，小人慌慌张张跑丢了。秀才就跟丢了魂一样，精神错乱，调养了半年才治好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;据说蒲松龄本人也练气功。&lt;/p&gt;
&lt;p&gt;故事本身就是很普通的志怪故事。&lt;/p&gt;
&lt;h3 id="尸变"&gt;尸变&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;老翁和儿子在村里开了家客店，黄昏时四个商贩来投宿，但是已经客满。四人请求无论如何收留，老翁寻思儿媳刚去世，儿子出门买棺材去了，那间灵堂可以住。&lt;/p&gt;
&lt;p&gt;四人于是住下。然而女尸竟然诈尸，朝客人吹气，只有一人没睡着逃了出来。女尸就追，那人路过老翁家，怕来不及就没敲门。&lt;/p&gt;
&lt;p&gt;那人往县城赶，经过一间寺庙敲门求救，里面僧人听得奇怪，不开，那人就与女尸在一棵大白杨旁边周旋，最后女尸抱着树僵硬了。&lt;/p&gt;
&lt;p&gt;和尚这才开门，把那人救了，报了县官。县官亲自勘验，那女尸手指插在树里，多叫了人手才拔出来。又叫人去老翁家，那边女尸没了，客人死了，正乱作一团，老翁跟着差役过来，把尸体抬了回去。&lt;/p&gt;
&lt;p&gt;幸存的那人怕回乡没人信，县官还给他写了证明文书。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是个灵异恐怖故事。&lt;/p&gt;
&lt;p&gt;故事的寓意我不敢断言，但网友却升起了赛博法庭。&lt;/p&gt;
&lt;p&gt;有的说，女尸不针对其他人，只针对投宿的四人，这四人肯定不是好玩意。（啊？其他人睡女尸旁边了吗？）&lt;/p&gt;
&lt;p&gt;有的说，古代男权社会压迫女性，她丈夫去买棺木，一天都没回，肯定有鬼，所以她才变成厉鬼了。（蒲松龄：来，笔给你）&lt;/p&gt;
&lt;p&gt;有的说，这四人是偷尸体的，尸变就是幸存的那个人编的。（嗯，有想象力是好事）&lt;/p&gt;
&lt;p&gt;还有的说，这是政治隐喻，这四人代表普通人，店家是统治者，女尸是官吏酷政，寺庙是超脱世人的精神寄托。（有趣，但为什么要用女尸来隐喻苛政？县官又算什么？）&lt;/p&gt;
&lt;p&gt;关于最大的疑点，儿媳死了却没人守灵，我来展开一些浅薄的分析：&lt;/p&gt;
&lt;p&gt;女的是“新死”，有多“新”？可能是刚死不久。丈夫去买棺木，没来得及回也可以理解，老头又要看店。而且守灵一般是晚辈给去世的长辈守灵。&lt;/p&gt;
&lt;p&gt;故事除了父子外，没提及家族其他人，可以认为没有子嗣。&lt;/p&gt;
&lt;p&gt;但即便如此，无人守灵也有些不妥。在我老家，一般会请亲戚和朋友，几个中年男人在逝者旁打一夜牌。&lt;/p&gt;
&lt;p&gt;就算客人坚决请求，老人也确实没有其他去处，住灵堂也有点抽象。&lt;/p&gt;</description></item><item><title>grep 使用中的一些坑</title><link>https://binac.org/posts/pitfalls-when-using-grep/</link><pubDate>Tue, 02 Jan 2024 00:29:21 +0800</pubDate><guid>https://binac.org/posts/pitfalls-when-using-grep/</guid><description>&lt;h2 id="换行符和回车符"&gt;换行符和回车符&lt;/h2&gt;
&lt;p&gt;首先我们知道，类 Unix 系统都习惯使用换行符 &lt;code&gt;\n&lt;/code&gt;（LF，Line Feed）换行，Mac OS 9 及之前则使用回车符
&lt;code&gt;\r&lt;/code&gt;（CR，Carriage Return），Windows 则使用 &lt;code&gt;\r\n&lt;/code&gt;（CR+LF）换行。&lt;/p&gt;
&lt;p&gt;而在 HTTP Header 中，也是以 CR+LF 换行。就算世界上大部分服务器托管在 Linux 上，它还是这么用了，别问，问就是规定。&lt;/p&gt;
&lt;p&gt;产生不必要的字符就算了，有时真叫人迷惑。当我在处理 Header 时，发现 grep 莫名其妙“无法匹配”。&lt;/p&gt;
&lt;p&gt;为了简化问题，打开终端试试：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ab\r\n&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;结果是显然的，为 &lt;code&gt;ab&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这个呢：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ab\r\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;^a&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;废话，还是 &lt;code&gt;ab&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;别急，这个呢：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ab\r\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;^a.*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果你输出“正常”，那么恭喜你，不用踩这个坑。但我还是建议看下去。&lt;/p&gt;
&lt;p&gt;如果你的结果是空行，哪天遇到，这破问题能卡老半天。&lt;/p&gt;
&lt;p&gt;其实仔细分析，如果 grep 不匹配，连空行都不会打印的。&lt;/p&gt;
&lt;p&gt;更别说用 &lt;code&gt;$?&lt;/code&gt; 检查返回值了，grep 返回值为 0，这说明根本就是匹配成功的。&lt;/p&gt;
&lt;p&gt;把这个问题丢给 DeepSeek 或者 GPT-4o，它们会一本正经地胡说八道。&lt;/p&gt;
&lt;p&gt;用以下命令查看真实数据：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ab\r\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;^a.*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; hexdump -C
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;00000000&lt;/span&gt; &lt;span class="m"&gt;61&lt;/span&gt; &lt;span class="m"&gt;62&lt;/span&gt; 0d 0a &lt;span class="p"&gt;|&lt;/span&gt;ab..&lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;00000004&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ab\r\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;^a.*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cat -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ab^M
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;因此 grep 确实匹配到了，莫名其妙就对用户不可见了。&lt;/p&gt;
&lt;p&gt;如果这个字符串本身不可见，那么问题来了，为什么 &lt;code&gt;printf 'ab\r\n'&lt;/code&gt; 可以正常打印？&lt;/p&gt;
&lt;p&gt;难道是因为终端有对控制字符的特殊处理，而管道不行？&lt;/p&gt;
&lt;p&gt;如果是管道的问题，为什么 &lt;code&gt;printf 'ab\r\n' | cat&lt;/code&gt; 可以打印？&lt;/p&gt;
&lt;p&gt;我还尝试了不同机器上包括 BSD 和 GNU 的 grep，都打印空行。&lt;/p&gt;
&lt;p&gt;好了，揭晓答案。&lt;/p&gt;
&lt;p&gt;最终，我注意到，为了方便查找文本，我在所有机器上都设置了
&lt;code&gt;alias grep=&amp;quot;grep --color=auto&amp;quot;&lt;/code&gt;，这样本身很正常，我相信很多人也是这么干的。&lt;/p&gt;
&lt;p&gt;问题就在于，grep 会对结果二次处理，用来显示颜色，回车后原来的字符串自然就被覆盖了！&lt;/p&gt;
&lt;p&gt;啊，这……&lt;/p&gt;
&lt;p&gt;那么问题来了，为什么 hexdump 结果正常？&lt;/p&gt;
&lt;p&gt;其实是因为 &lt;code&gt;grep --color=auto&lt;/code&gt; 会自动处理颜色，当不与终端输出挂钩时，是不会二次处理颜色的。&lt;/p&gt;
&lt;p&gt;比如，&lt;code&gt;printf 'ab\r\n' | grep '^a.*' | cat&lt;/code&gt; 就可以得到匹配文本。&lt;/p&gt;
&lt;p&gt;当使用 &lt;code&gt;--color=always&lt;/code&gt; 时，就会强行处理颜色：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ab\r\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep --color&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="s1"&gt;&amp;#39;^.*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; hexdump -C
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;00000000&lt;/span&gt; 1b 5b &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt; 3b &lt;span class="m"&gt;33&lt;/span&gt; &lt;span class="m"&gt;31&lt;/span&gt; 6d 1b 5b 4b &lt;span class="m"&gt;61&lt;/span&gt; &lt;span class="m"&gt;62&lt;/span&gt; 0d 1b 5b &lt;span class="p"&gt;|&lt;/span&gt;.&lt;span class="o"&gt;[&lt;/span&gt;01&lt;span class="p"&gt;;&lt;/span&gt;31m.&lt;span class="o"&gt;[&lt;/span&gt;Kab..&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;00000010&lt;/span&gt; 6d 1b 5b 4b 0a &lt;span class="p"&gt;|&lt;/span&gt;m.&lt;span class="o"&gt;[&lt;/span&gt;K.&lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;00000015&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;或者：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ab\r\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep --color&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="s1"&gt;&amp;#39;^.*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cat -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^&lt;span class="o"&gt;[[&lt;/span&gt;01&lt;span class="p"&gt;;&lt;/span&gt;31m^&lt;span class="o"&gt;[[&lt;/span&gt;Kab^M^&lt;span class="o"&gt;[[&lt;/span&gt;m^&lt;span class="o"&gt;[[&lt;/span&gt;K
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;草，破案了……&lt;/p&gt;
&lt;h2 id="不要使用-grep--v-来检查文件是否不包含特定字符串"&gt;不要使用 grep -v 来检查文件是否不包含特定字符串&lt;/h2&gt;
&lt;p&gt;刷到 StackOverflow 上一个回答&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;：&lt;/p&gt;
&lt;span class="image-container"&gt;&lt;span class="link" &gt;&lt;a href="answer.png"
target="_blank"&gt;&lt;img loading="lazy" class="img" src="answer.png"/&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;p&gt;回答没毛病，但是底下这个点评的 Tom Harrison 言之凿凿地说，可以使用 &lt;code&gt;-v&lt;/code&gt; 来反转条件……真是如此？&lt;/p&gt;
&lt;p&gt;假设有一个文件 &lt;code&gt;test&lt;/code&gt;，内容如下：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;a
b
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;先查找文件中&lt;strong&gt;不存在&lt;/strong&gt;的字符串 &lt;code&gt;c&lt;/code&gt; 试试：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep c test&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;! grep c test&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -v c test&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;似乎没问题，两种都对。&lt;/p&gt;
&lt;p&gt;再查找文件中&lt;strong&gt;存在&lt;/strong&gt;的字符串 &lt;code&gt;a&lt;/code&gt; 呢：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep a test&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;! grep a test&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -v a test&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;扯犊子了吧？&lt;/p&gt;
&lt;p&gt;原因就在于，&lt;code&gt;-v&lt;/code&gt; 反转的是查找条件，而不是查找结果。GNU grep 的手册是这么解释 &lt;code&gt;-v&lt;/code&gt; 的：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; -v, --invert-match
Invert the sense of matching, to select non-matching lines.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;-v&lt;/code&gt; 会匹配那些不包含 &lt;code&gt;a&lt;/code&gt; 的行，自然 &lt;code&gt;b&lt;/code&gt; 就被查找出来了。&lt;/p&gt;
&lt;p&gt;你可以说这人的意思是 &lt;code&gt;-v&lt;/code&gt; 可以反转查找条件，问题是这里是 &lt;code&gt;if&lt;/code&gt; 语句，要根据结果的条件来判断，然后进行其他操作，明显这人以为 &lt;code&gt;-v&lt;/code&gt; 和 &lt;code&gt;!&lt;/code&gt;
是一样的效果。这种点评带有很大的误导性。&lt;/p&gt;
&lt;p&gt;那么 &lt;code&gt;-v&lt;/code&gt; 能不能实现类似的功能呢？其实是可以的，只不过要结合自己的使用情境配合复杂一点的正则罢了，没必要。&lt;/p&gt;
&lt;p&gt;多句嘴，StackOverflow 这个网站虽然很有用，但是对用户并不友好。&lt;/p&gt;
&lt;p&gt;我连点赞的权限都没有，还有很多人在瞎点评，而且还是错误的。&lt;/p&gt;
&lt;p&gt;有时好不容易搜到一个和自己类似问题，然后就有人点评让你去看某些要么屁用没有要么一堆冗余信息的链接，还说不要重复提问 blah blah……贼烦这种伪精英主义。&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/57158235"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/57158235&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>视频相关的命令和脚本</title><link>https://binac.org/posts/video-related-commands-and-scripts/</link><pubDate>Wed, 22 Nov 2023 17:03:15 +0800</pubDate><guid>https://binac.org/posts/video-related-commands-and-scripts/</guid><description>&lt;h2 id="ffmpeg-实用命令"&gt;FFmpeg 实用命令&lt;/h2&gt;
&lt;h3 id="剪切视频而不重新转码"&gt;剪切视频而不重新转码&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -ss 00:00:30.0 -to 00:00:40.0 -i input.mp4 -c copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;-ss&lt;/code&gt; 和 &lt;code&gt;-to&lt;/code&gt; 分别指定起始时间。&lt;/p&gt;
&lt;p&gt;这种方式输出的视频有一定问题，最好还是转码（不要用 &lt;code&gt;-c copy&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 id="codec-not-currently-supported-in-container"&gt;codec not currently supported in container&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;一些视频流和音频流无法放到同一容器中，例如 WMA 音频不能与 H.264 视频兼容，解决方式是将音频也转码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.wmv -c:v libx264 -c:a aac output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="旋转视频而不转码"&gt;旋转视频而不转码&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;逆时针&lt;/strong&gt;旋转 90 度：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -display_rotation &lt;span class="m"&gt;90&lt;/span&gt; -i input.mp4 -c copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="裁切视频"&gt;裁切视频&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -vf &lt;span class="s2"&gt;&amp;#34;crop=out_w:out_h:x:y&amp;#34;&lt;/span&gt; -c:a copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;crop=&lt;/code&gt; 后以 &lt;code&gt;:&lt;/code&gt; 分隔的四个参数分别表示：输出宽度、输出高度、左上角横坐标、左上角纵坐标。&lt;/p&gt;
&lt;h3 id="调整分辨率"&gt;调整分辨率&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;将高度调整为 1080，比例不变：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -vf &lt;span class="nv"&gt;scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-1:1080 -c:a copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="循环重复"&gt;循环重复&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;循环重复 3 次：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -stream_loop &lt;span class="m"&gt;3&lt;/span&gt; -i input.mp4 -c copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="合并多个同类文件而不转码"&gt;合并多个同类文件而不转码&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;使用一个文本保存需合并的文件路径，如 &lt;code&gt;input.txt&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;file &amp;#39;path/to/part1.mp4&amp;#39;
file &amp;#39;path/to/part2.mp4&amp;#39;
file &amp;#39;path/to/part3.mp4&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -f concat -i input.txt -c copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="去隔行扫描"&gt;去隔行扫描&lt;sup id="fnref:8"&gt;&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref"&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.vob -vf yadif -c:a copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="视频加速减速"&gt;视频加速/减速&lt;sup id="fnref:9"&gt;&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref"&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;加速两倍：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -vf &lt;span class="s2"&gt;&amp;#34;setpts=0.5*PTS&amp;#34;&lt;/span&gt; output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;减速一半：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -vf &lt;span class="s2"&gt;&amp;#34;setpts=2*PTS&amp;#34;&lt;/span&gt; output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="视频反转"&gt;视频反转&lt;sup id="fnref:10"&gt;&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref"&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;仅反转视频：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -vf reverse output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;同时反转视频和音频：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -vf reverse -af areverse output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="hev1-转-hvc1"&gt;hev1 转 hvc1&lt;sup id="fnref:11"&gt;&lt;a href="#fn:11" class="footnote-ref" role="doc-noteref"&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/h3&gt;
&lt;p&gt;一些 HEVC 在 macOS 上无法显示缩略图，也无法用原生 APP 打开，这是因为使用了 hev1：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ ffprobe -v error -select_streams v -show_entries &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;codec_tag_string -of &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;noprint_wrappers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1:nokey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; input.mp4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hev1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;转换为 hvc1 即可：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -tag:v hvc1 -c copy output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ ffprobe -v error -select_streams v -show_entries &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;codec_tag_string -of &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;noprint_wrappers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1:nokey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hvc1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="脚本"&gt;脚本&lt;/h2&gt;
&lt;h3 id="列出码率最高的视频"&gt;列出码率最高的视频&lt;/h3&gt;
&lt;p&gt;默认列出前 10 个，不支持含换行符的文件名。&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;-v&lt;/code&gt; 选项以首个视频流码率列出（而不是总体码率），但不支持 MKV、TS、WebM 等容器。&lt;/p&gt;
&lt;figure style="margin:0 0 1.5em 0"&gt;
&lt;figcaption style="text-align: right"&gt;
&lt;a href="https://binac.org/posts/video-related-commands-and-scripts/top-videos-by-bitrate.sh" style="color: lightslategrey;font-size: 0.8em"&gt;top-videos-by-bitrate.sh&lt;/a&gt;
&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;span class="lnt"&gt;72
&lt;/span&gt;&lt;span class="lnt"&gt;73
&lt;/span&gt;&lt;span class="lnt"&gt;74
&lt;/span&gt;&lt;span class="lnt"&gt;75
&lt;/span&gt;&lt;span class="lnt"&gt;76
&lt;/span&gt;&lt;span class="lnt"&gt;77
&lt;/span&gt;&lt;span class="lnt"&gt;78
&lt;/span&gt;&lt;span class="lnt"&gt;79
&lt;/span&gt;&lt;span class="lnt"&gt;80
&lt;/span&gt;&lt;span class="lnt"&gt;81
&lt;/span&gt;&lt;span class="lnt"&gt;82
&lt;/span&gt;&lt;span class="lnt"&gt;83
&lt;/span&gt;&lt;span class="lnt"&gt;84
&lt;/span&gt;&lt;span class="lnt"&gt;85
&lt;/span&gt;&lt;span class="lnt"&gt;86
&lt;/span&gt;&lt;span class="lnt"&gt;87
&lt;/span&gt;&lt;span class="lnt"&gt;88
&lt;/span&gt;&lt;span class="lnt"&gt;89
&lt;/span&gt;&lt;span class="lnt"&gt;90
&lt;/span&gt;&lt;span class="lnt"&gt;91
&lt;/span&gt;&lt;span class="lnt"&gt;92
&lt;/span&gt;&lt;span class="lnt"&gt;93
&lt;/span&gt;&lt;span class="lnt"&gt;94
&lt;/span&gt;&lt;span class="lnt"&gt;95
&lt;/span&gt;&lt;span class="lnt"&gt;96
&lt;/span&gt;&lt;span class="lnt"&gt;97
&lt;/span&gt;&lt;span class="lnt"&gt;98
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;BRIEF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;VIDEO_EXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3gp avi flv m4v mkv mov mp4 mpeg mpg ts vob webm wmv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;NUM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;VIDEO_BITRATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;USAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cat &lt;span class="s"&gt;&amp;lt;&amp;lt;-END
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Usage: $SCRIPT [&amp;lt;options&amp;gt;] &amp;lt;path&amp;gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;List top videos by bitrate.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -b do not prepend bitrate to output lines
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -e &amp;lt;ext&amp;gt; video file extensions, separated by spaces, case-insensitive
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; defaults to &amp;#39;$VIDEO_EXT&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -n &amp;lt;num&amp;gt; list top num video files
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; defaults to &amp;#39;$NUM&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -v list by the first video stream bitrate instead of the overall bitrate
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; not applicable for containers like MKV, TS, WebM, etc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -h display this help and exit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Home page: &amp;lt;https://binac.org/posts/video-related-commands-and-scripts/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;END&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;_exit&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bhve:n:&amp;#34;&lt;/span&gt; c&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; b&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;BRIEF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; e&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VIDEO_EXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; n&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;NUM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VIDEO_BITRATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; h&lt;span class="o"&gt;)&lt;/span&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *&lt;span class="o"&gt;)&lt;/span&gt; _exit &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is_integer&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -eq &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; 2&amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is_integer &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NUM&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; _exit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;OPTIND &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _exit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;min&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -le &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;get_bitrate&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VIDEO_BITRATE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ffprobe -v error -show_entries &lt;span class="nv"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bit_rate -of &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;noprint_wrappers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1:nokey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ffprobe -v error -select_streams v:0 -show_entries &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bit_rate -of &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;noprint_wrappers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1:nokey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -n &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TMP_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;mktemp&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rm -f &amp;#34;$TMP_FILE&amp;#34;&amp;#39;&lt;/span&gt; EXIT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;grep_expression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &lt;/span&gt;&lt;span class="nv"&gt;$VIDEO_EXT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;&amp;#39;s/ /\$|\\./g&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cut -c3-&lt;span class="k"&gt;)&lt;/span&gt;$&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -type f &lt;span class="p"&gt;|&lt;/span&gt; grep -i -E &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$grep_expression&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r f&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; : &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Detecting videos: %s\r&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;br&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;get_bitrate &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; is_integer &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$br&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;numfmt --to iec &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$br&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;\n%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Cannot get bitrate: &lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wc -l &amp;lt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;\n&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;No videos found&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Detected &lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt; videos&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error &lt;span class="s2"&gt;&amp;#34;Top &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;min &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NUM&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; videos by bitrate:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BRIEF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%-16s%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Bitrate&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;File&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sort -t &lt;span class="s1"&gt;&amp;#39;|&amp;#39;&lt;/span&gt; -k1 -hr &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NUM&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;br&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="p"&gt;%%|*&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="p"&gt;#*|&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BRIEF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%-16s%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$br&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ top-videos-by-bitrate.sh -n &lt;span class="m"&gt;3&lt;/span&gt; /path/to/videos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Detecting videos: &lt;span class="m"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Detected &lt;span class="m"&gt;100&lt;/span&gt; videos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Top &lt;span class="m"&gt;3&lt;/span&gt; videos by bitrate:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Bitrate File
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;8.8M /path/to/videos/1.mp4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4.2M /path/to/videos/2/3.avi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3.8M /path/to/videos/4/5/6.mkv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="列出占用空间最大的视频"&gt;列出占用空间最大的视频&lt;/h3&gt;
&lt;p&gt;使用 du 命令检测磁盘占用空间（而不是实际大小）。&lt;/p&gt;
&lt;p&gt;默认列出前 10 个，不支持含换行符的文件名。&lt;/p&gt;
&lt;figure style="margin:0 0 1.5em 0"&gt;
&lt;figcaption style="text-align: right"&gt;
&lt;a href="https://binac.org/posts/video-related-commands-and-scripts/top-videos-by-size.sh" style="color: lightslategrey;font-size: 0.8em"&gt;top-videos-by-size.sh&lt;/a&gt;
&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;span class="lnt"&gt;64
&lt;/span&gt;&lt;span class="lnt"&gt;65
&lt;/span&gt;&lt;span class="lnt"&gt;66
&lt;/span&gt;&lt;span class="lnt"&gt;67
&lt;/span&gt;&lt;span class="lnt"&gt;68
&lt;/span&gt;&lt;span class="lnt"&gt;69
&lt;/span&gt;&lt;span class="lnt"&gt;70
&lt;/span&gt;&lt;span class="lnt"&gt;71
&lt;/span&gt;&lt;span class="lnt"&gt;72
&lt;/span&gt;&lt;span class="lnt"&gt;73
&lt;/span&gt;&lt;span class="lnt"&gt;74
&lt;/span&gt;&lt;span class="lnt"&gt;75
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;BRIEF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;VIDEO_EXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3gp avi flv m4v mkv mov mp4 mpeg mpg ts vob webm wmv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;NUM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;USAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cat &lt;span class="s"&gt;&amp;lt;&amp;lt;-END
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Usage: $SCRIPT [&amp;lt;options&amp;gt;] &amp;lt;path&amp;gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;List top videos by size.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -b do not prepend size to output lines
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -e &amp;lt;ext&amp;gt; video file extensions, separated by spaces, case-insensitive
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; defaults to &amp;#39;$VIDEO_EXT&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -n &amp;lt;num&amp;gt; list top num video files
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; defaults to &amp;#39;$NUM&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -h display this help and exit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Home page: &amp;lt;https://binac.org/posts/video-related-commands-and-scripts/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;END&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;_exit&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bhe:n:&amp;#34;&lt;/span&gt; c&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; b&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;BRIEF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; e&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VIDEO_EXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; n&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;NUM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; h&lt;span class="o"&gt;)&lt;/span&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *&lt;span class="o"&gt;)&lt;/span&gt; _exit &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is_integer&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -eq &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; 2&amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is_integer &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NUM&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; _exit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;OPTIND &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _exit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;min&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -le &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TMP_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;mktemp&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rm -f &amp;#34;$TMP_FILE&amp;#34;&amp;#39;&lt;/span&gt; EXIT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;grep_expression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &lt;/span&gt;&lt;span class="nv"&gt;$VIDEO_EXT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;&amp;#39;s/ /\$|\\./g&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; cut -c3-&lt;span class="k"&gt;)&lt;/span&gt;$&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -type f -exec du -h &lt;span class="o"&gt;{}&lt;/span&gt; + &lt;span class="p"&gt;|&lt;/span&gt; grep -i -E &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$grep_expression&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sort -h -r &amp;gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wc -l &amp;lt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;No videos found&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Detected &lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt; videos&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error &lt;span class="s2"&gt;&amp;#34;Top &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;min &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NUM&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; videos by size:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BRIEF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%-16s%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Size&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;File&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;head -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$NUM&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$TMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;sz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="p"&gt;%% *&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="p"&gt;#* &lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BRIEF&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%-16s%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$sz&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ top-videos-by-size.sh -n &lt;span class="m"&gt;3&lt;/span&gt; /path/to/videos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Detected &lt;span class="m"&gt;100&lt;/span&gt; videos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Top &lt;span class="m"&gt;3&lt;/span&gt; videos by size:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Size File
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4.2G /path/to/videos/1.mp4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;420M /path/to/videos/2/3.avi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;42M /path/to/videos/4/5/6.mkv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="hevc-转码"&gt;HEVC 转码&lt;/h3&gt;
&lt;p&gt;将文件批量转为 HEVC 编码，只转码首条视频流，其他视频流、音频流、字幕流原封不动。&lt;/p&gt;
&lt;p&gt;默认会忽略本身已经是 hevc/av1/vp9 编码的文件，因为这些编码已经效率很高了。使用 &lt;code&gt;-c &amp;lt;codecs&amp;gt;&lt;/code&gt; 指定需要忽略的编码。&lt;/p&gt;
&lt;figure style="margin:0 0 1.5em 0"&gt;
&lt;figcaption style="text-align: right"&gt;
&lt;a href="https://binac.org/posts/video-related-commands-and-scripts/hevcize.sh" style="color: lightslategrey;font-size: 0.8em"&gt;hevcize.sh&lt;/a&gt;
&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt; 10
&lt;/span&gt;&lt;span class="lnt"&gt; 11
&lt;/span&gt;&lt;span class="lnt"&gt; 12
&lt;/span&gt;&lt;span class="lnt"&gt; 13
&lt;/span&gt;&lt;span class="lnt"&gt; 14
&lt;/span&gt;&lt;span class="lnt"&gt; 15
&lt;/span&gt;&lt;span class="lnt"&gt; 16
&lt;/span&gt;&lt;span class="lnt"&gt; 17
&lt;/span&gt;&lt;span class="lnt"&gt; 18
&lt;/span&gt;&lt;span class="lnt"&gt; 19
&lt;/span&gt;&lt;span class="lnt"&gt; 20
&lt;/span&gt;&lt;span class="lnt"&gt; 21
&lt;/span&gt;&lt;span class="lnt"&gt; 22
&lt;/span&gt;&lt;span class="lnt"&gt; 23
&lt;/span&gt;&lt;span class="lnt"&gt; 24
&lt;/span&gt;&lt;span class="lnt"&gt; 25
&lt;/span&gt;&lt;span class="lnt"&gt; 26
&lt;/span&gt;&lt;span class="lnt"&gt; 27
&lt;/span&gt;&lt;span class="lnt"&gt; 28
&lt;/span&gt;&lt;span class="lnt"&gt; 29
&lt;/span&gt;&lt;span class="lnt"&gt; 30
&lt;/span&gt;&lt;span class="lnt"&gt; 31
&lt;/span&gt;&lt;span class="lnt"&gt; 32
&lt;/span&gt;&lt;span class="lnt"&gt; 33
&lt;/span&gt;&lt;span class="lnt"&gt; 34
&lt;/span&gt;&lt;span class="lnt"&gt; 35
&lt;/span&gt;&lt;span class="lnt"&gt; 36
&lt;/span&gt;&lt;span class="lnt"&gt; 37
&lt;/span&gt;&lt;span class="lnt"&gt; 38
&lt;/span&gt;&lt;span class="lnt"&gt; 39
&lt;/span&gt;&lt;span class="lnt"&gt; 40
&lt;/span&gt;&lt;span class="lnt"&gt; 41
&lt;/span&gt;&lt;span class="lnt"&gt; 42
&lt;/span&gt;&lt;span class="lnt"&gt; 43
&lt;/span&gt;&lt;span class="lnt"&gt; 44
&lt;/span&gt;&lt;span class="lnt"&gt; 45
&lt;/span&gt;&lt;span class="lnt"&gt; 46
&lt;/span&gt;&lt;span class="lnt"&gt; 47
&lt;/span&gt;&lt;span class="lnt"&gt; 48
&lt;/span&gt;&lt;span class="lnt"&gt; 49
&lt;/span&gt;&lt;span class="lnt"&gt; 50
&lt;/span&gt;&lt;span class="lnt"&gt; 51
&lt;/span&gt;&lt;span class="lnt"&gt; 52
&lt;/span&gt;&lt;span class="lnt"&gt; 53
&lt;/span&gt;&lt;span class="lnt"&gt; 54
&lt;/span&gt;&lt;span class="lnt"&gt; 55
&lt;/span&gt;&lt;span class="lnt"&gt; 56
&lt;/span&gt;&lt;span class="lnt"&gt; 57
&lt;/span&gt;&lt;span class="lnt"&gt; 58
&lt;/span&gt;&lt;span class="lnt"&gt; 59
&lt;/span&gt;&lt;span class="lnt"&gt; 60
&lt;/span&gt;&lt;span class="lnt"&gt; 61
&lt;/span&gt;&lt;span class="lnt"&gt; 62
&lt;/span&gt;&lt;span class="lnt"&gt; 63
&lt;/span&gt;&lt;span class="lnt"&gt; 64
&lt;/span&gt;&lt;span class="lnt"&gt; 65
&lt;/span&gt;&lt;span class="lnt"&gt; 66
&lt;/span&gt;&lt;span class="lnt"&gt; 67
&lt;/span&gt;&lt;span class="lnt"&gt; 68
&lt;/span&gt;&lt;span class="lnt"&gt; 69
&lt;/span&gt;&lt;span class="lnt"&gt; 70
&lt;/span&gt;&lt;span class="lnt"&gt; 71
&lt;/span&gt;&lt;span class="lnt"&gt; 72
&lt;/span&gt;&lt;span class="lnt"&gt; 73
&lt;/span&gt;&lt;span class="lnt"&gt; 74
&lt;/span&gt;&lt;span class="lnt"&gt; 75
&lt;/span&gt;&lt;span class="lnt"&gt; 76
&lt;/span&gt;&lt;span class="lnt"&gt; 77
&lt;/span&gt;&lt;span class="lnt"&gt; 78
&lt;/span&gt;&lt;span class="lnt"&gt; 79
&lt;/span&gt;&lt;span class="lnt"&gt; 80
&lt;/span&gt;&lt;span class="lnt"&gt; 81
&lt;/span&gt;&lt;span class="lnt"&gt; 82
&lt;/span&gt;&lt;span class="lnt"&gt; 83
&lt;/span&gt;&lt;span class="lnt"&gt; 84
&lt;/span&gt;&lt;span class="lnt"&gt; 85
&lt;/span&gt;&lt;span class="lnt"&gt; 86
&lt;/span&gt;&lt;span class="lnt"&gt; 87
&lt;/span&gt;&lt;span class="lnt"&gt; 88
&lt;/span&gt;&lt;span class="lnt"&gt; 89
&lt;/span&gt;&lt;span class="lnt"&gt; 90
&lt;/span&gt;&lt;span class="lnt"&gt; 91
&lt;/span&gt;&lt;span class="lnt"&gt; 92
&lt;/span&gt;&lt;span class="lnt"&gt; 93
&lt;/span&gt;&lt;span class="lnt"&gt; 94
&lt;/span&gt;&lt;span class="lnt"&gt; 95
&lt;/span&gt;&lt;span class="lnt"&gt; 96
&lt;/span&gt;&lt;span class="lnt"&gt; 97
&lt;/span&gt;&lt;span class="lnt"&gt; 98
&lt;/span&gt;&lt;span class="lnt"&gt; 99
&lt;/span&gt;&lt;span class="lnt"&gt;100
&lt;/span&gt;&lt;span class="lnt"&gt;101
&lt;/span&gt;&lt;span class="lnt"&gt;102
&lt;/span&gt;&lt;span class="lnt"&gt;103
&lt;/span&gt;&lt;span class="lnt"&gt;104
&lt;/span&gt;&lt;span class="lnt"&gt;105
&lt;/span&gt;&lt;span class="lnt"&gt;106
&lt;/span&gt;&lt;span class="lnt"&gt;107
&lt;/span&gt;&lt;span class="lnt"&gt;108
&lt;/span&gt;&lt;span class="lnt"&gt;109
&lt;/span&gt;&lt;span class="lnt"&gt;110
&lt;/span&gt;&lt;span class="lnt"&gt;111
&lt;/span&gt;&lt;span class="lnt"&gt;112
&lt;/span&gt;&lt;span class="lnt"&gt;113
&lt;/span&gt;&lt;span class="lnt"&gt;114
&lt;/span&gt;&lt;span class="lnt"&gt;115
&lt;/span&gt;&lt;span class="lnt"&gt;116
&lt;/span&gt;&lt;span class="lnt"&gt;117
&lt;/span&gt;&lt;span class="lnt"&gt;118
&lt;/span&gt;&lt;span class="lnt"&gt;119
&lt;/span&gt;&lt;span class="lnt"&gt;120
&lt;/span&gt;&lt;span class="lnt"&gt;121
&lt;/span&gt;&lt;span class="lnt"&gt;122
&lt;/span&gt;&lt;span class="lnt"&gt;123
&lt;/span&gt;&lt;span class="lnt"&gt;124
&lt;/span&gt;&lt;span class="lnt"&gt;125
&lt;/span&gt;&lt;span class="lnt"&gt;126
&lt;/span&gt;&lt;span class="lnt"&gt;127
&lt;/span&gt;&lt;span class="lnt"&gt;128
&lt;/span&gt;&lt;span class="lnt"&gt;129
&lt;/span&gt;&lt;span class="lnt"&gt;130
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ARGUMENTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;CODEC_WHITELIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hevc av1 vp9&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;AVAILABLE_ENCODERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ffmpeg -codecs 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;^.\{8\}hevc&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed -n &lt;span class="s1"&gt;&amp;#39;s/.*(encoders: \([^)]*\)).*/\1/p&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ENCODER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;libx265
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;USAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cat &lt;span class="s"&gt;&amp;lt;&amp;lt;-END
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Usage: $SCRIPT [&amp;lt;options&amp;gt;] &amp;lt;file&amp;gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;List top videos by size.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -a &amp;lt;args&amp;gt; pass extra arguments to ffmpeg
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -c &amp;lt;codecs&amp;gt; videos with these codecs will NOT be encoded
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; defaults to &amp;#39;$CODEC_WHITELIST&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -e &amp;lt;encoder&amp;gt; set HEVC encoder, this could accelerate transcoding but reduce the quality
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; you may need to use &amp;#39;-a &amp;lt;args&amp;gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; available encoders: $AVAILABLE_ENCODERS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; defaults to &amp;#39;$ENCODER&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -o &amp;lt;dir&amp;gt; set output directory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; by default, the output file will be placed in the same directory as the original video
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; -h display this help and exit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Home page: &amp;lt;https://binac.org/posts/video-related-commands-and-scripts/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;END&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;THIN_LINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%.s-&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;seq &lt;span class="m"&gt;1&lt;/span&gt; 80&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;BOLD_LINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%.s=&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;seq &lt;span class="m"&gt;1&lt;/span&gt; 80&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%s\n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;_exit&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ha:c:e:o:&amp;#34;&lt;/span&gt; c&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; a&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;ARGUMENTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; c&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;CODEC_WHITELIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; e&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;ENCODER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; o&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; h&lt;span class="o"&gt;)&lt;/span&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$USAGE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *&lt;span class="o"&gt;)&lt;/span&gt; _exit &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;contains&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -qs &lt;span class="s2"&gt;&amp;#34;\&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;\&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; ! contains &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$AVAILABLE_ENCODERS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ENCODER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Invalid encoder: &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$ENCODER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; _exit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ARGUMENTS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ENCODER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; libx265 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;ARGUMENTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-x265-params log-level=error&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; ! -d &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; ! -w &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Cannot access: &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;realpath &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;OPTIND &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt; -eq &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _exit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;do_ffmpeg&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -f &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;File already exists: &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; ffmpeg -nostdin -v error -hide_banner -stats -i &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -map &lt;span class="m"&gt;0&lt;/span&gt; -c copy -c:v:0 &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ENCODER&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;$ARGUMENTS&lt;/span&gt; -tag:v:0 hvc1 &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;==&amp;gt; &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; rm -f &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;FAIL_LIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;mktemp&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rm -f &amp;#34;$FAIL_LIST&amp;#34;&amp;#39;&lt;/span&gt; EXIT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$#&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; f in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$THIN_LINE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;==&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="s2"&gt;: &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; : &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;vc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ffprobe &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -v error -select_streams v:0 -show_entries &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;codec_name -of &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;noprint_wrappers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1:nokey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -n 1&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$vc&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Unknown input: &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$FAIL_LIST&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; contains &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$CODEC_WHITELIST&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$vc&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Skipping codec: &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$vc&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$OUTPUT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.HEVC.mp4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;.HEVC.mp4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; do_ffmpeg &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Unable to use MP4 container, retrying with MKV&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.mkv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; do_ffmpeg &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Unable to use MKV container&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Skipping file: &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$FAIL_LIST&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -s &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$FAIL_LIST&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BOLD_LINE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; error &lt;span class="s2"&gt;&amp;#34;Unable to encode the following &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wc -l &amp;lt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$FAIL_LIST&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; files:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cat &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$FAIL_LIST&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;h4 id="已知问题"&gt;已知问题&lt;/h4&gt;
&lt;p&gt;一些视频除了视频流、音频流、字幕流外，还带有数据流、附件流，这些流可能无法在转码后的容器中支持（即使转码前后容器相同）&lt;sup id="fnref:12"&gt;&lt;a href="#fn:12" class="footnote-ref" role="doc-noteref"&gt;12&lt;/a&gt;&lt;/sup&gt;。以下是一个容器无法支持数据流的报错：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[mp4 @ 0x55af0c59d0c0] Could not find tag for codec none in stream #3, codec not currently supported in container
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument
Error initializing output stream 0:0 --
Unable to use MP4 container, retrying with MKV
[matroska @ 0x55ceccef70c0] Only audio, video, and subtitles are supported for Matroska.
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument
Error initializing output stream 0:0 --
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;此时可以手动转码并使用 &lt;code&gt;-map -0:d&lt;/code&gt; 丢弃数据流：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i input.mp4 -map &lt;span class="m"&gt;0&lt;/span&gt; -map -0:d -c copy -c:v libx265 output.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;另外，由于脚本只使用 MP4 和 MKV 尝试，也可能存在不支持的音频流和字幕流等&lt;sup id="fnref:13"&gt;&lt;a href="#fn:13" class="footnote-ref" role="doc-noteref"&gt;13&lt;/a&gt;&lt;/sup&gt;，此时也可以按需转码或丢弃，例如音频转码
&lt;code&gt;-c:a acc&lt;/code&gt;，丢弃字幕 &lt;code&gt;-map -0:s&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id="示例"&gt;示例&lt;/h4&gt;
&lt;h5 id="批量转码"&gt;批量转码&lt;/h5&gt;
&lt;p&gt;支持通配符：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hevcize.sh /path/to/videos/1.mp4 /path/to/videos/2/3.avi /path/to/videos/4/5/6.mkv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hevcize.sh /path/to/videos/*.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;将 &lt;code&gt;/path/to/videos&lt;/code&gt; 下所有文件进行转码，结果保存在当前目录：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find /path/to/videos -type f -exec hevcize.sh -o . &lt;span class="o"&gt;{}&lt;/span&gt; +
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h5 id="显卡加速"&gt;显卡加速&lt;/h5&gt;
&lt;p&gt;比如 NVIDIA 显卡可以使用 hevc_nvenc 编码器进行加速，Intel 显卡使用 hevc_qsv，这些都需要硬件和驱动支持。&lt;/p&gt;
&lt;p&gt;macOS 使用 hevc_videotoolbox 编码器进行显卡加速（&lt;code&gt;hevcize.sh -h&lt;/code&gt; 查看有哪些可用编码器）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ hevecize.sh -e hevc_videotoolbox /path/to/videos/1.mp4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; 1/1: &lt;span class="s1"&gt;&amp;#39;/path/to/videos/1.mp4&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt; &lt;span class="nv"&gt;fps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="nv"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-0.0 &lt;span class="nv"&gt;Lsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; 15000kB &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;00:05:00.00 &lt;span class="nv"&gt;bitrate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; 500.0kbits/s &lt;span class="nv"&gt;dup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;33&lt;/span&gt; &lt;span class="nv"&gt;drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt; &lt;span class="nv"&gt;speed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.00x
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; &lt;span class="s1"&gt;&amp;#39;/path/to/videos/1.mp4.HEVC.mp4&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;显卡加速转码很快，CPU 占用率低，但比起默认的 libx265（CPU 软编码）质量较差。&lt;/p&gt;
&lt;p&gt;通过 &lt;code&gt;-a &amp;lt;args&amp;gt;&lt;/code&gt; 可以传递更多参数给 ffmpeg。&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://superuser.com/a/141343"target="_blank" rel="noopener noreferrer"&gt;https://superuser.com/a/141343&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://superuser.com/a/432379"target="_blank" rel="noopener noreferrer"&gt;https://superuser.com/a/432379&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/31683689"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/31683689&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a href="https://video.stackexchange.com/a/4571"target="_blank" rel="noopener noreferrer"&gt;https://video.stackexchange.com/a/4571&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;&lt;a href="https://superuser.com/a/624564"target="_blank" rel="noopener noreferrer"&gt;https://superuser.com/a/624564&lt;/a&gt;&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/51002586"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/51002586&lt;/a&gt;&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/49373401"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/49373401&lt;/a&gt;&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;&lt;a href="https://video.stackexchange.com/a/17397"target="_blank" rel="noopener noreferrer"&gt;https://video.stackexchange.com/a/17397&lt;/a&gt;&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;&lt;a href="https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video"target="_blank" rel="noopener noreferrer"&gt;https://trac.ffmpeg.org/wiki/How%20to%20speed%20up%20/%20slow%20down%20a%20video&lt;/a&gt;&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;&lt;a href="https://video.stackexchange.com/a/17739"target="_blank" rel="noopener noreferrer"&gt;https://video.stackexchange.com/a/17739&lt;/a&gt;&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:11"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/46540577"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/46540577&lt;/a&gt;&amp;#160;&lt;a href="#fnref:11" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:12"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/a/69529533"target="_blank" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/69529533&lt;/a&gt;&amp;#160;&lt;a href="#fnref:12" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:13"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Comparison_of_video_container_formats"target="_blank" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Comparison_of_video_container_formats&lt;/a&gt;&amp;#160;&lt;a href="#fnref:13" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>在 Linux 上使用 Exim4 发送邮件</title><link>https://binac.org/posts/sending-emails-with-exim4-on-linux/</link><pubDate>Fri, 12 May 2023 00:40:40 +0800</pubDate><guid>https://binac.org/posts/sending-emails-with-exim4-on-linux/</guid><description>&lt;p&gt;带有 GUI 的 Linux 可以直接使用集成的邮件客户端，比如 Thunderbird。但在服务器上，发送邮件需要一些配置，踩一些坑。&lt;/p&gt;
&lt;p&gt;邮件系统由三个部分组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mail User Agent (MUA)，发送和接收邮件。&lt;/li&gt;
&lt;li&gt;Mail Transfer Agent (MTA)，在计算机之间传输邮件。&lt;/li&gt;
&lt;li&gt;Mail Delivery Agent (MDA)，将收到的邮件投递到用户收件箱。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MUA 我使用的是 GNU Mailutils，MTA/MDA 使用的是 Exim4。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;下面以网易 163 邮箱为例，&lt;/del&gt; 说明怎么在 Debian 上配置 Exim4。&lt;/p&gt;
&lt;p&gt;脑瘫的网易把我服务器 IP 屏蔽了，发邮件就报错 &lt;code&gt;553 authentication is required&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;换 QQ 邮箱了，&lt;/del&gt; 苟曰的腾讯也把我屏蔽了，改 Gmail&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; 了，但是教程对网易和 QQ 也适用，前提是你的 IP 没被屏蔽。&lt;/p&gt;
&lt;p&gt;先安装：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install exim4 mailutils
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="smtp-端口号"&gt;SMTP 端口号&lt;/h2&gt;
&lt;h3 id="25"&gt;25&lt;/h3&gt;
&lt;p&gt;25 端口最初作为明文传输的 SMTP 端口，现在经常被 ISP 和云服务器提供商屏蔽。&lt;/p&gt;
&lt;p&gt;使用 telnet 检测端口是否可用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;telnet smtp.163.com &lt;span class="m"&gt;25&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;个人测试发现，家用电信宽带、Vultr 可用，AWS 不可用。&lt;/p&gt;
&lt;h3 id="465"&gt;465&lt;/h3&gt;
&lt;p&gt;465 是非标准的 SMTPS 端口，SMTPS 实际上就是 SMTP + TLS，类似 HTTPS 和 HTTP
的关系。虽然不是标准端口，但确实有一些邮件提供商支持。&lt;/p&gt;
&lt;h3 id="587"&gt;587&lt;/h3&gt;
&lt;p&gt;587 是现在比较通用的标准端口，使用 STARTTLS 加密通信。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;从上面我们可以知道，规范的 SMTP 端口为 25，TLS 版本为 465，STARTTLS 版本为 587。&lt;/p&gt;
&lt;p&gt;后两者支持加密通信，不过只要服务端有支持，25 端口也可以加密通信，各邮件提供商有不同的实现。&lt;/p&gt;
&lt;p&gt;可以在 &lt;a href="https://esmtp.email/tools/tls/"target="_blank" rel="noopener noreferrer"&gt;https://esmtp.email/tools/tls/&lt;/a&gt; 检测各端口的加密支持情况。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这个网站的服务出现错误，可以通过下面的 openssl 方式检测。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果以下命令有 TLS 握手并输出证书，说明支持 TLS：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl s_client -connect smtp.gmail.com:465 &amp;lt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果以下命令输出证书，说明支持 STARTTLS：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl s_client -connect smtp.gmail.com:587 -starttls smtp &amp;lt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="gmail"&gt;Gmail&lt;/h3&gt;
&lt;p&gt;Gmail 使用标准的 STARTTLS 587 端口，和非标准的 SMTPS 465 端口。&lt;/p&gt;
&lt;h3 id="qq-邮箱"&gt;QQ 邮箱&lt;/h3&gt;
&lt;p&gt;和 Gmail 一样，即 465 支持 SMTPS，587 支持 STARTTLS。&lt;/p&gt;
&lt;h3 id="网易邮箱"&gt;网易邮箱&lt;/h3&gt;
&lt;p&gt;网易比较坑爹，它确实支持 25、465、587 三个端口，但只有 25 支持 STARTTLS，后两者都是 SMTPS，而它显然没有在官网进行相关说明。&lt;/p&gt;
&lt;h2 id="配置-exim4"&gt;配置 Exim4&lt;/h2&gt;
&lt;p&gt;以 root 运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg-reconfigure exim4-config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这会打开一个 TUI 配置向导。&lt;/p&gt;
&lt;p&gt;依次设置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;mail sent by smarthost; received via SMTP or fetchmail&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;System mail name&lt;/code&gt; 保持 &lt;code&gt;localhost&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;IP-addresses to listen on for incoming SMTP connections&lt;/code&gt; 保持默认的环回地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Other destinations for which mail is accepted&lt;/code&gt; 留空&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Machines to relay mail for&lt;/code&gt; 留空&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;IP address or host name of the outgoing smarthost&lt;/code&gt; 填入 &lt;code&gt;smtp.gmail.com::587&lt;/code&gt;
，注意两个半角冒号，这里使用 STARTTLS，配置比 SMTPS 简单。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果用 QQ 邮箱，就用 &lt;code&gt;smtp.qq.com::587&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果用网易 163，就用 &lt;code&gt;smtp.163.com::465&lt;/code&gt;，这里的 465 是网易支持的 SMTPS 端口，587
也可以，因为网易这逗比俩端口都是 SMTPS，效果完全一样。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Hide local mail name in outgoing mail&lt;/code&gt; 如果选择 &lt;code&gt;NO&lt;/code&gt;，那么本地发件人不会隐藏（例如
root@localhost 之类），&lt;code&gt;YES&lt;/code&gt; 则会改为邮箱地址。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其余选项按喜好配置即可。&lt;/p&gt;
&lt;p&gt;需要注意的是，出于安全原因，root 用户不能接收邮件，因此通常设置别名转发到常规用户。&lt;/p&gt;
&lt;p&gt;这需要设置 &lt;code&gt;/etc/aliases&lt;/code&gt;，此外还可以设置当常规用户收到邮件时，将邮件投递到某个互联网邮箱，例如：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root: admin
admin: admin@example.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;运行 &lt;code&gt;newaliases&lt;/code&gt; 使其生效。当系统收到邮件时（例如 root 用户的 Cron 定时任务），&lt;code&gt;admin@example.com&lt;/code&gt;
就可以收到邮件提醒了。&lt;/p&gt;
&lt;p&gt;接着在 &lt;code&gt;/etc/exim4/passwd.client&lt;/code&gt; 中配置邮箱和密码：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# QQ
smtp.qq.com:username@qq.com:password
# 163
smtp.163.com:username@163.com:password
# Gmail
*.gmail.com:username@gmail.com:password
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;对于 QQ 和网易，这里的密码都要用授权码，登录 &lt;a href="https://mail.qq.com"target="_blank" rel="noopener noreferrer"&gt;https://mail.qq.com&lt;/a&gt; &lt;a href="https://mail.163.com"target="_blank" rel="noopener noreferrer"&gt;https://mail.163.com&lt;/a&gt; 获取。&lt;/p&gt;
&lt;p&gt;Gmail 可以使用类似的专用密码 &lt;a href="https://support.google.com/accounts/answer/185833"target="_blank" rel="noopener noreferrer"&gt;https://support.google.com/accounts/answer/185833&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;*.gmail.com&lt;/code&gt; 是 Gmail SMTP 服务器包含通配符的域名，如果以下查询有类似的别名（CNAME），要改成匹配的，例如
&lt;code&gt;gmail*.google.com&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ host smtp.gmail.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;smtp.gmail.com is an &lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; gmail-smtp-msa.l.google.com.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gmail-smtp-msa.l.google.com has address 74.125.127.109
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gmail-smtp-msa.l.google.com has address 74.125.127.108
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接着配置本地用户与发件邮箱的映射关系。&lt;/p&gt;
&lt;p&gt;先在 &lt;code&gt;/etc/hostname&lt;/code&gt; 和 &lt;code&gt;/etc/hosts&lt;/code&gt; 中查看主机名和 FQDN/PQDN（一般就是 hosts 文件中 &lt;code&gt;127.0.1.1&lt;/code&gt;
对应的域名），或者通过 &lt;code&gt;hostname&lt;/code&gt; 和 &lt;code&gt;hostname -f&lt;/code&gt; 获取。普通用户没有公网可解析的 FQDN，此时 FQDN 其实是
PQDN，与主机名相同。&lt;/p&gt;
&lt;p&gt;下面以主机名 &lt;code&gt;myhostname&lt;/code&gt;、FQDN &lt;code&gt;myhostname.mylocaldomain&lt;/code&gt; 为例，编辑
&lt;code&gt;/etc/email-addresses&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root: username@gmail.com
root@localhost: username@gmail.com
root@myhostname: username@gmail.com
root@myhostname.mylocaldomain: username@gmail.com
admin: username@gmail.com
admin@localhost: username@gmail.com
admin@myhostname: username@gmail.com
admin@myhostname.mylocaldomain: username@gmail.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这一步对于 Gmail 可能不是必须的，因为 Gmail 会自动重写发件人。但是其他邮件就不一定了。&lt;/p&gt;
&lt;p&gt;如果在之前 &lt;code&gt;Hide local mail name in outgoing mail&lt;/code&gt; 的设置中选择了 &lt;code&gt;YES&lt;/code&gt;，这一步或许可以省略。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;首先声明我不是邮件专家。&lt;/p&gt;
&lt;p&gt;有段时间，Cron 任务发送的邮件如果不设置 &lt;code&gt;MAILFROM&lt;/code&gt; 为邮件地址就无法发送，网易和 QQ 都出现过。在一次更新软件包后，把
&lt;code&gt;MAILFROM&lt;/code&gt; 去掉，结果又可以了。不确定到底是网易或 QQ 的问题，还是 Cron 或 Exim4
的问题，又或者是别的什么问题。毕竟就像我说的，我不是邮件专家。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;***真有邮件专家吗？为什么有这么蛋疼的配置？&lt;/del&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;更新配置并重启服务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;update-exim4.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;service exim4 restart
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;发个邮件试试：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;邮件内容&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; mail -s &lt;span class="s2"&gt;&amp;#34;邮件标题&amp;#34;&lt;/span&gt; admin@example.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;日志位于 &lt;code&gt;/var/log/exim4/mainlog&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果你是 STARTTLS，那应该已经发送成功了。如果日志里有 TLS 没有正确关闭的报错：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;H=smtp.gmail.com [2607:f8b0:400e:c01::6c] TLS error on connection (recv): The TLS connection was non-properly terminated.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;属于正常现象，网易、QQ 也有。这是因为服务器有意省略 close_notify 以节省往返时间，尽管这违反了 TLS 协议&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;。尾注链接是
hestiacp 开发者给 Exim4 提交的 PR，然而 Exim4 对 GitHub 不感兴趣，不知道什么时候才能合并。如果你使用的工具会封禁错误日志中的
IP，你可能需要自行编译 Exim4。&lt;/p&gt;
&lt;p&gt;SMTPS 还需要接下来的一些配置。&lt;/p&gt;
&lt;h2 id="smtps-配置"&gt;SMTPS 配置&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/h2&gt;
&lt;p&gt;STARTTLS 请忽略此步骤。&lt;/p&gt;
&lt;p&gt;创建 &lt;code&gt;/etc/exim4/exim4.conf.localmacros&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;MAIN_TLS_ENABLE = 1
REMOTE_SMTP_SMARTHOST_HOSTS_REQUIRE_TLS = *
TLS_ON_CONNECT_PORTS = 465
REQUIRE_PROTOCOL = smtps
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意端口 465 与上面一致。&lt;/p&gt;
&lt;p&gt;编辑 &lt;code&gt;/etc/exim4/exim4.conf.template&lt;/code&gt;，在
&lt;code&gt;.ifdef REMOTE_SMTP_SMARTHOST_HOSTS_REQUIRE_TLS ... .endif&lt;/code&gt; 后（注意 &lt;code&gt;.endif&lt;/code&gt;）添加：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;.ifdef REQUIRE_PROTOCOL
protocol = REQUIRE_PROTOCOL
.endif
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 &lt;code&gt;.ifdef MAIN_TLS_ENABLE&lt;/code&gt; 后添加：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;.ifdef TLS_ON_CONNECT_PORTS
tls_on_connect_ports = TLS_ON_CONNECT_PORTS
.endif
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;更新配置并重启服务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;update-exim4.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;service exim4 restart
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;运行 &lt;code&gt;exim4 -qff&lt;/code&gt; 强制发送之前发送失败而暂存的邮件。&lt;/p&gt;
&lt;p&gt;这下终于可以了。&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://wiki.debian.org/Exim4Gmail"target="_blank" rel="noopener noreferrer"&gt;https://wiki.debian.org/Exim4Gmail&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://github.com/Exim/exim/pull/94"target="_blank" rel="noopener noreferrer"&gt;https://github.com/Exim/exim/pull/94&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://wiki.debian.org/Exim#Communicating_with_a_smarthost"target="_blank" rel="noopener noreferrer"&gt;https://wiki.debian.org/Exim#Communicating_with_a_smarthost&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Homebrew 使用中的一些问题</title><link>https://binac.org/posts/some-issues-when-using-homebrew/</link><pubDate>Fri, 31 Mar 2023 22:55:23 +0800</pubDate><guid>https://binac.org/posts/some-issues-when-using-homebrew/</guid><description>&lt;h2 id="no-cask-quarantine-support-available-swift-runtime-error-或-listxattr-for-destination-failed-2"&gt;No Cask quarantine support available: Swift runtime error 或 listxattr for destination failed: 2&lt;/h2&gt;
&lt;p&gt;遇到这个问题，首先要确定 CommandLineTools 是否安装了正确版本，查看 &lt;code&gt;brew doctor&lt;/code&gt; 的提示。&lt;/p&gt;
&lt;p&gt;如果安装正确，有可能是使用 OCLP 安装 Sequoia 导致，传递给 Swift 脚本的参数不正确，受影响的包括：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/usr/local/Homebrew/Library/Homebrew/cask/utils/copy-xattrs.swift
/usr/local/Homebrew/Library/Homebrew/cask/utils/quarantine.swift
/usr/local/Homebrew/Library/Homebrew/cask/utils/trash.swift
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;以我个人使用经验，这不仅影响安装 Cask，还会导致卸载 Cask 时 CommandLineTools 莫名其妙损坏。&lt;/p&gt;
&lt;p&gt;解决方案见此&lt;a href="https://github.com/orgs/Homebrew/discussions/5482#discussioncomment-15682533"target="_blank" rel="noopener noreferrer"&gt;讨论&lt;/a&gt;，但他的&lt;a href="https://github.com/ajorpheus/homebrew-oclp-patches"target="_blank" rel="noopener noreferrer"&gt;仓库&lt;/a&gt;中使用了不换行空格
&lt;code&gt;U+00A0&lt;/code&gt;，会导致 Swift 报错。我替换为常规空格并提了 PR，还没合并。&lt;/p&gt;
&lt;p&gt;以下是可以直接使用的 patch：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://qianbinbin.github.io/posts/some-issues-when-using-homebrew/homebrew-oclp.patch &lt;span class="p"&gt;|&lt;/span&gt; git -C &lt;span class="k"&gt;$(&lt;/span&gt;brew --repository&lt;span class="k"&gt;)&lt;/span&gt; apply
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;details&gt;
&lt;summary&gt;原解决方案（已过时）&lt;/summary&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/Library/Developer/CommandLineTools/usr/bin/swift-frontend, -frontend, -interpret, /usr/local/Homebrew/Library/Homebrew/cask/utils/copy-xattrs.swift, -target, x86_64-apple-macosx15, -enable-objc-interop, -stack-check, -sdk, /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk, -color-diagnostics, -new-driver-path, /Library/Developer/CommandLineTools/usr/bin/swift-driver, -empty-abi-descriptor, -resource-dir, /Library/Developer/CommandLineTools/usr/lib/swift, -module-name, main, -disable-clang-spi, -target-sdk-version, 15.5, -target-sdk-name, macosx15.5, -external-plugin-path, /Library/Developer/Developer/usr/lib/swift/host/plugins#/Library/Developer/Developer/usr/bin/swift-plugin-server, -external-plugin-path, /Library/Developer/Developer/usr/local/lib/swift/host/plugins#/Library/Developer/Developer/usr/bin/swift-plugin-server, -in-process-plugin-server-path, /Library/Developer/CommandLineTools/usr/lib/swift/host/libSwiftInProcPluginServer.dylib, -plugin-path, /Library/Developer/CommandLineTools/usr/lib/swift/host/plugins, -plugin-path, /Library/Developer/CommandLineTools/usr/local/lib/swift/host/plugins, --, /usr/local/Caskroom/visual-studio-code/1.104.1/Visual Studio Code.app, /Applications/Visual Studio Code.app
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;copy-xattrs.swift&lt;/code&gt; 期望两个参数，即源目录和目标目录，但最终传递的打印出来很长（参数可能与系统或 CLT 版本有关）。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--&lt;/code&gt; 及其之前的参数应该被移除，刚好保留两个，但不知为何没有。这是 OCLP 的 bug。&lt;/p&gt;
&lt;p&gt;一个临时解决方案：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/Library/Homebrew/cask/utils/copy-xattrs.swift b/Library/Homebrew/cask/utils/copy-xattrs.swift
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index 794242ed13..9d43c906fb 100755
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/Library/Homebrew/cask/utils/copy-xattrs.swift
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/Library/Homebrew/cask/utils/copy-xattrs.swift
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -15,7 +15,7 @@ guard CommandLine.arguments.count &amp;gt;= 3 else {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; exit(2)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;-CommandLine.arguments[2].withCString { destinationPath in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+CommandLine.arguments.last!.withCString { destinationPath in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; let destinationNamesLength = listxattr(destinationPath, nil, 0, 0)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if destinationNamesLength == -1 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; print(&amp;#34;listxattr for destination failed: \(errno)&amp;#34;, to: &amp;amp;SwiftErr.stream)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -40,7 +40,7 @@ CommandLine.arguments[2].withCString { destinationPath in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; destinationNamesBuffer.deallocate()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- CommandLine.arguments[1].withCString { sourcePath in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ CommandLine.arguments.dropLast().last!.withCString { sourcePath in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; let sourceNamesLength = listxattr(sourcePath, nil, 0, 0)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if sourceNamesLength == -1 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; print(&amp;#34;listxattr for source failed: \(errno)&amp;#34;, to: &amp;amp;SwiftErr.stream)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/details&gt;
&lt;h2 id="downloadclfjetbrainscomcn-证书不受信任"&gt;&lt;code&gt;download.clf.jetbrains.com.cn&lt;/code&gt; 证书不受信任&lt;/h2&gt;
&lt;p&gt;安装 JetBrains 家的 3A 大作时，例如 pycharm-ce，会从 &lt;code&gt;download.jetbrains.com&lt;/code&gt; 下载，从国内访问会重定向到
&lt;code&gt;download.clf.jetbrains.com.cn&lt;/code&gt;，然后重定向到 &lt;code&gt;download-cdn.jetbrains.com.cn&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;问题是如果使用 brew 版 curl（通过环境变量
&lt;code&gt;HOMEBREW_FORCE_BREWED_CURL=1&lt;/code&gt;），&lt;code&gt;download.clf.jetbrains.com.cn&lt;/code&gt; 的证书链不受信任：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;原因是 &lt;code&gt;download.clf.jetbrains.com.cn&lt;/code&gt; 使用了 Cloudflare 服务，证书链最终依赖的根证书颁发机构
&lt;code&gt;AAA Certificate Services&lt;/code&gt;
&lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1957685"target="_blank" rel="noopener noreferrer"&gt;在 2025 年四月被 Mozilla 移除&lt;/a&gt;，而
Mozilla 又被多个下游使用，如
ca-certificates、curl、&lt;a href="https://github.com/certifi/python-certifi/issues/349"target="_blank" rel="noopener noreferrer"&gt;certifi&lt;/a&gt;
等。具体可以运行
&lt;code&gt;openssl s_client -connect download.clf.jetbrains.com.cn:443 -showcerts&lt;/code&gt; 查看证书链。&lt;/p&gt;
&lt;p&gt;然而令人震惊的是，时隔这么长时间居然都没有修复。&lt;/p&gt;
&lt;p&gt;尽管如此，只要不依赖新版证书就可以访问，例如系统内置 curl，或者指定新版 curl 使用系统内置证书
&lt;code&gt;SSL_CERT_FILE=/etc/ssl/cert.pem&lt;/code&gt;，或者直接信任该域名。以下介绍几种解决办法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先当然可以让 brew 临时使用系统内置 curl（版本不能太新）。但是我的内置 curl 有点古老，不支持目前一些服务器已经要求的 TLS
1.3，每次切回去也麻烦。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把 &lt;code&gt;download.jetbrains.com&lt;/code&gt; 加入代理，自动重定向到境外服务器，就不会跳转到
&lt;code&gt;download.clf.jetbrains.com.cn&lt;/code&gt; 了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;del&gt;先让 brew 安装，获取 URL 和缓存路径，用 curl 手动下载文件，然后重新安装即可：&lt;/del&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ brew upgrade pycharm-ce --cask --debug --verbose
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; Downloading https://download.jetbrains.com/python/pycharm-community-2025.1.3.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; Downloading from https://download.clf.jetbrains.com.cn/python/pycharm-community-2025.1.3.dmg?Key-Pair-Id&lt;span class="o"&gt;=&lt;/span&gt;KP3KOP89OO9G7&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;Expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1751722972&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;Signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dEnUsSeV~PS3oII1M6613eB90R
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/bin/env /usr/local/Homebrew/Library/Homebrew/shims/shared/curl --disable --cookie /dev/null --globoff --show-error --user-agent Homebrew/4.5.8-138-g94606f3-dirty&lt;span class="se"&gt;\ \(&lt;/span&gt;Macintosh&lt;span class="se"&gt;\;\ &lt;/span&gt;Intel&lt;span class="se"&gt;\ &lt;/span&gt;Mac&lt;span class="se"&gt;\ &lt;/span&gt;OS&lt;span class="se"&gt;\ &lt;/span&gt;X&lt;span class="se"&gt;\ &lt;/span&gt;10.15.7&lt;span class="se"&gt;\)\ &lt;/span&gt;curl/8.14.1 --header Accept-Language:&lt;span class="se"&gt;\ &lt;/span&gt;en --fail --progress-bar --retry &lt;span class="m"&gt;3&lt;/span&gt; --remote-time --output /Users/qianbinbin/Library/Caches/Homebrew/downloads/06b43bd257e6ea2fe184885df215ab7d22f5c33845d2f439284faa538602e4da--pycharm-community-2025.1.3.dmg.incomplete --location https://download.clf.jetbrains.com.cn/python/pycharm-community-2025.1.3.dmg&lt;span class="se"&gt;\?&lt;/span&gt;Key-Pair-Id&lt;span class="o"&gt;=&lt;/span&gt;KP3KOP89OO9G7&lt;span class="se"&gt;\&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;Expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1751722972&lt;span class="se"&gt;\&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;Signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dEnUsSeV&lt;span class="se"&gt;\~&lt;/span&gt;PS3oII1M6613eB90RM2SNn18hIeXHuJbNhIiKN-7b0p6RSP5J2m8hGJHBV5orYV-w52kHbdyIb7rdCrVxsblrEE6kUfGF8f-GX9wa1Kw0UKRLiNr7HyzgCLkDPDbscFw&lt;span class="se"&gt;\~&lt;/span&gt;oyURmnS7lijpefqJPoCzKT3r5UP&lt;span class="se"&gt;\~&lt;/span&gt;ifpyUI2Sf3zEozoaF69Nvx0k9mobFF6MJPHCAfz9WNZaSXkejEgfUyMHKrNp6PS6&lt;span class="se"&gt;\~&lt;/span&gt;BjerZ4IIhgjriTNntqlWNWvU7u65QjefXe3W8eeTS5DmHVqUMU4332N7xRAV4cBDoHKICOabQqcWGEqwqk9hyYeF92Jd3gXJagSFj4eRHAjsqnw__
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="c1"&gt;# 新版 curl 可以使用 -k 进行不安全连接&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ /usr/bin/curl -L -o /Users/qianbinbin/Library/Caches/Homebrew/downloads/06b43bd257e6ea2fe184885df215ab7d22f5c33845d2f439284faa538602e4da--pycharm-community-2025.1.3.dmg.incomplete https://download.jetbrains.com/python/pycharm-community-2025.1.3.dmg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ brew upgrade pycharm-ce --cask
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;此方法已不可行，brew 近期已经必须要能连接到服务器，才会去校验下载的文件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;直接信任该域名证书链。&lt;/p&gt;
&lt;p&gt;设置环境变量 &lt;code&gt;HOMEBREW_FORCE_BREWED_CA_CERTIFICATES=1&lt;/code&gt;，这会让 curl 读取
&lt;code&gt;$(brew --prefix)/etc/ca-certificates/cert.pem&lt;/code&gt; 文件以及同目录下的证书。brew 调用 curl
实际上执行的是一个包装脚本，位于
&lt;code&gt;$(brew --repository)/Library/Homebrew/shims/shared/curl&lt;/code&gt;，它里面有证书相关的细节。&lt;/p&gt;
&lt;p&gt;然后运行以下脚本将证书下载到该目录下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;download.clf.jetbrains.com.cn
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;brew --prefix&lt;span class="k"&gt;)&lt;/span&gt;/etc/ca-certificates/&lt;span class="nv"&gt;$domain&lt;/span&gt;.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl s_client -connect &lt;span class="nv"&gt;$domain&lt;/span&gt;:443 -showcerts &amp;lt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; awk &lt;span class="s1"&gt;&amp;#39;/-----BEGIN CERTIFICATE-----/{flag=1} flag{print} /-----END CERTIFICATE-----/{flag=0}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$cert&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ln -s &lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="nv"&gt;$cert&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;brew --prefix&lt;span class="k"&gt;)&lt;/span&gt;/etc/ca-certificates/&lt;span class="k"&gt;$(&lt;/span&gt;openssl x509 -noout -hash -in &lt;span class="nv"&gt;$cert&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;现在 brew 已经可以正常下载了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="编译-go-项目超时"&gt;编译 Go 项目超时&lt;/h2&gt;
&lt;p&gt;众所周知，国内网络环境编译 Go 项目会超时，只要设置国内镜像即可：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;GO111MODULE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;GOPROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://goproxy.cn,direct
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;del&gt;我想了十天十夜也不明白，源码服务器也要屏蔽？多少有点脑瘫哦。&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;然而即使设置了镜像，brew 在编译 Go 项目时也&lt;strong&gt;不会读取用户设置&lt;/strong&gt;，于是也会超时：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;==&amp;gt; go build -o=/usr/local/Cellar/v2ray/5.3.0/libexec/v2ray -ldflags=-s -w -buildid= ./main
Last 15 lines from /Users/username/Library/Logs/Homebrew/v2ray/01.go:
proxy/vmess/encoding/auth.go:8:2: golang.org/x/crypto@v0.4.0: Get &amp;#34;https://proxy.golang.org/golang.org/x/crypto/@v/v0.4.0.zip&amp;#34;: dial tcp 142.251.42.241:443: i/o timeout
common/protocol/dns/io.go:7:2: golang.org/x/net@v0.4.0: Get &amp;#34;https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
app/dns/nameserver_quic.go:14:2: golang.org/x/net@v0.4.0: Get &amp;#34;https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
transport/internet/http/hub.go:11:2: golang.org/x/net@v0.4.0: Get &amp;#34;https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
common/strmatcher/matchers.go:9:2: golang.org/x/net@v0.4.0: Get &amp;#34;https://proxy.golang.org/golang.org/x/net/@v/v0.4.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
common/protocol/headers.go:6:2: golang.org/x/sys@v0.3.0: Get &amp;#34;https://proxy.golang.org/golang.org/x/sys/@v/v0.3.0.zip&amp;#34;: dial tcp [2404:6800:4012:2::2011]:443: i/o timeout
transport/internet/filelocker_other.go:9:2: golang.org/x/sys@v0.3.0: Get &amp;#34;https://proxy.golang.org/golang.org/x/sys/@v/v0.3.0.zip&amp;#34;: dial tcp [2404:6800:4012:2::2011]:443: i/o timeout
app/commander/commander.go:10:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/dial.go:13:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
app/instman/command/command_grpc.pb.go:6:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/dial.go:14:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/dial.go:15:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
transport/internet/grpc/encoding/conn.go:13:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
app/commander/service.go:7:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
app/instman/command/command_grpc.pb.go:7:2: google.golang.org/grpc@v1.51.0: Get &amp;#34;https://proxy.golang.org/google.golang.org/grpc/@v/v1.51.0.zip&amp;#34;: dial tcp 142.251.43.17:443: i/o timeout
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;brew 编译时实际上调用的是 Ruby 脚本，那就可以直接修改脚本。以 &lt;code&gt;v2ray&lt;/code&gt; 为例，在
&lt;code&gt;$(brew --repository homebrew/core)&lt;/code&gt; 目录应用以下 patch：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/Formula/v2ray.rb b/Formula/v2ray.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index 297d0759440..baef313f96f 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/Formula/v2ray.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/Formula/v2ray.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -39,6 +39,9 @@ class V2ray &amp;lt; Formula
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; def install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ ENV[&amp;#39;GO111MODULE&amp;#39;] = &amp;#39;on&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ ENV[&amp;#39;GOPROXY&amp;#39;] = &amp;#39;https://goproxy.cn,direct&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ldflags = &amp;#34;-s -w -buildid=&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; system &amp;#34;go&amp;#34;, &amp;#34;build&amp;#34;, *std_go_args(ldflags: ldflags, output: libexec/&amp;#34;v2ray&amp;#34;), &amp;#34;./main&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="perl-版本过低"&gt;Perl 版本过低&lt;/h2&gt;
&lt;p&gt;此类问题没有统一的错误日志，但是查看编译日志，例如 &lt;code&gt;~/Library/Logs/Homebrew/openssl@3/04.make&lt;/code&gt;，如果看到一堆关于
perl 的错误&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;，就基本确定是 perl 版本的问题了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ grep &lt;span class="s1"&gt;&amp;#39;you may need to install&amp;#39;&lt;/span&gt; ~/Library/Logs/Homebrew/openssl@3/04.make
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_certtypeext.t ...................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_comp.t ..........................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_key_share.t .....................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_npn.t ...........................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslcbcpadding.t .................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslcertstatus.t .................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslextension.t ..................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslmessages.t ...................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslsigalgs.t ....................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslsessiontick.t ................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslsignature.t ..................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslskewith0p.t ..................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslversions.t ...................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_sslvertol.t .....................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tls13alerts.t ...................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tls13cookie.t ...................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tls13downgrade.t ................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tls13hrr.t ......................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tls13kexmodes.t .................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tls13messages.t .................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Can&lt;span class="s1"&gt;&amp;#39;t locate IO/Socket/IP.pm in @INC (you may need to install the IO::Socket::IP module) (@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib) at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tls13psk.t ......................
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Can&amp;#39;&lt;/span&gt;t locate IO/Socket/IP.pm in @INC &lt;span class="o"&gt;(&lt;/span&gt;you may need to install the IO::Socket::IP module&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;@INC contains: /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/Configurations . /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl /Library/Perl/5.18/darwin-thread-multi-2level /Library/Perl/5.18 /Network/Library/Perl/5.18/darwin-thread-multi-2level /Network/Library/Perl/5.18 /Library/Perl/Updates/5.18.4 /System/Library/Perl/5.18/darwin-thread-multi-2level /System/Library/Perl/5.18 /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level /System/Library/Perl/Extras/5.18 /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/external/perl/Text-Template-1.56/lib&lt;span class="o"&gt;)&lt;/span&gt; at /private/tmp/opensslA3-20250703-82846-1idqq/openssl-3.5.1/util/perl/TLSProxy/Proxy.pm line 100.70-test_tlsextms.t ......................
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这种情况可以尝试加入 &lt;code&gt;depends_on &amp;quot;perl&amp;quot;&lt;/code&gt;（运行时依赖） 或
&lt;code&gt;depends_on &amp;quot;perl&amp;quot; =&amp;gt; :build&lt;/code&gt;（编译时依赖），让软件依赖 brew 版 perl：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/Formula/o/openssl@3.rb b/Formula/o/openssl@3.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index af7fd717b72..8a966060f04 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/Formula/o/openssl@3.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/Formula/o/openssl@3.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -23,6 +23,7 @@ class OpensslAT3 &amp;lt; Formula
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; depends_on &amp;#34;ca-certificates&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ depends_on &amp;#34;perl&amp;#34; =&amp;gt; :build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; on_linux do
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; resource &amp;#34;Test::Harness&amp;#34; do
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这样就可以编译了。&lt;/p&gt;
&lt;p&gt;不过，&lt;a href="https://github.com/openssl/openssl/pull/27941"target="_blank" rel="noopener noreferrer"&gt;openssl 已经在修复 perl 版本问题&lt;/a&gt;，毕竟他们的文档里说只需
perl 5.10 就行。&lt;/p&gt;
&lt;p&gt;如果不想依赖 brew 版 perl，要应用此 patch，在脚本中加入断点：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/Formula/o/openssl@3.rb b/Formula/o/openssl@3.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index af7fd717b72..b502996acc0 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/Formula/o/openssl@3.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/Formula/o/openssl@3.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -107,6 +107,7 @@ class OpensslAT3 &amp;lt; Formula
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; system &amp;#34;make&amp;#34;, &amp;#34;install&amp;#34;, &amp;#34;MANDIR=#{man}&amp;#34;, &amp;#34;MANSUFFIX=ssl&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # AF_ALG support isn&amp;#39;t always enabled (e.g. some containers), which breaks the tests.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # AF_ALG is a kernel feature and failures are unlikely to be issues with the formula.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ raise &amp;#34;DEBUG BREAKPOINT&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; system &amp;#34;make&amp;#34;, &amp;#34;HARNESS_JOBS=#{ENV.make_jobs}&amp;#34;, &amp;#34;test&amp;#34;, &amp;#34;TESTS=-test_afalg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # Prevent `brew` from pruning the `certs` and `private` directories.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后执行 &lt;code&gt;brew install openssl@3.rb --debug&lt;/code&gt;，到 make test 前一步自动停止，进入 shell，下载并应用
patch：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -LO https://github.com/openssl/openssl/pull/27941.diff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git apply 27941.diff
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后退出并继续即可。&lt;/p&gt;
&lt;p&gt;但并不是所有的 perl 版本问题都可以这么简单解决，例如下面的 git 编译问题，就需要大体了解 git 构建过程。&lt;/p&gt;
&lt;h2 id="t-git-credential-netrcsh-no-such-file-or-directory"&gt;./t-git-credential-netrc.sh: No such file or directory&lt;/h2&gt;
&lt;p&gt;编译安装 git 时出现错误：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ~/Library/Logs/Homebrew/git/05.make&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/Applications/Xcode.app/Contents/Developer/usr/bin/make -C ../../.. &lt;span class="nv"&gt;SCRIPT_PERL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;contrib/credential/netrc/git-credential-netrc.perl&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; build-perl-script
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; * new perl-specific parameters
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;INSTLIBDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/private/tmp/git-20250521-61559-k2ku4o/git-2.49.0/.brew_home/share/perl5&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;INSTLIBDIR_EXTRA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/Applications/Xcode.app/Contents/Developer/Library/Perl/5.18/darwin-thread-multi-2level:/Library/Developer/CommandLineTools/Library/Perl/5.18/darwin-thread-multi-2level&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;INSTLIBDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$INSTLIBDIR&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INSTLIBDIR_EXTRA&lt;/span&gt;&lt;span class="p"&gt;:+:&lt;/span&gt;&lt;span class="nv"&gt;$INSTLIBDIR_EXTRA&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sed -e &lt;span class="s1"&gt;&amp;#39;s=@PATHSEP@=:=g&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -e &lt;span class="s2"&gt;&amp;#34;s=@INSTLIBDIR@=&lt;/span&gt;&lt;span class="nv"&gt;$INSTLIBDIR&lt;/span&gt;&lt;span class="s2"&gt;=g&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -e &lt;span class="s1"&gt;&amp;#39;s=@PERLLIBDIR_REL@=share/perl5=g&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -e &lt;span class="s1"&gt;&amp;#39;s=@GITEXECDIR_REL@=libexec/git-core=g&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -e &lt;span class="s1"&gt;&amp;#39;s=@LOCALEDIR_REL@=share/locale=g&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; perl/header_templates/fixed_prefix.template.pl &amp;gt;GIT-PERL-HEADER+ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mv GIT-PERL-HEADER+ GIT-PERL-HEADER
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/bin/sh generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER &lt;span class="s2"&gt;&amp;#34;contrib/credential/netrc/git-credential-netrc.perl&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;contrib/credential/netrc/git-credential-netrc+&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mv contrib/credential/netrc/git-credential-netrc+ contrib/credential/netrc/git-credential-netrc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./t-git-credential-netrc.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;?!ERR?! ./t-git-credential-netrc.sh: No such file or directory
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ok &lt;span class="m"&gt;1&lt;/span&gt; - &lt;span class="nb"&gt;set&lt;/span&gt; up &lt;span class="nb"&gt;test&lt;/span&gt; repository
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;not ok &lt;span class="m"&gt;2&lt;/span&gt; - git-credential-netrc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# perl &amp;#34;$GIT_SOURCE_DIR&amp;#34;/contrib/credential/netrc/test.pl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# failed 1 among 2 test(s)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1..2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make: *** &lt;span class="o"&gt;[&lt;/span&gt;test&lt;span class="o"&gt;]&lt;/span&gt; Error &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;提示找不到 &lt;code&gt;./t-git-credential-netrc.sh&lt;/code&gt; 这个文件，这个错误让人很容易以为是路径问题。&lt;/p&gt;
&lt;p&gt;从 2.47.1 版本以后就这样，就先 pin 了，以为过一段时间会有 brew 或 git 维护人员发现并修复，然而都 2.49.0 了还是不行。&lt;/p&gt;
&lt;p&gt;如果在 git 源码 &lt;code&gt;contrib/credential/netrc&lt;/code&gt; 目录下运行 &lt;code&gt;make testverbose&lt;/code&gt;，会出现以下错误：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Perl v5.26.0 required--this is only v5.18.4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;继续查阅源码可知
&lt;a href="https://github.com/git/git/commit/702d8c1f3b5377a64670b0f22add157b0bfc33dd"target="_blank" rel="noopener noreferrer"&gt;git 从 2.48 就开始要求 perl 5.26 了&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;Catalina 自带的 /usr/bin/perl 只有 5.18，brew 编译 git 时并不使用 brew 版 perl，而是系统自带，于是我这个
Catalina 老同志就被偷袭了。&lt;/p&gt;
&lt;p&gt;如果指定较新的 perl &lt;code&gt;export PERL_PATH=/usr/local/bin/perl&lt;/code&gt;，就可以正常编译。&lt;/p&gt;
&lt;p&gt;对此，考虑以下几种解决方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;如果不追求新版，可以 pin 在 2.47.1。&lt;/p&gt;
&lt;p&gt;如果还没有用 brew 安装 git，本地有 homebrew-core 源码的话，可以提取历史版本并编译：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git show aecf26f53d0:Formula/g/git.rb &amp;gt;git.rb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install ./git.rb --formula
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm ./git.rb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;或者去&lt;a href="https://github.com/Homebrew/homebrew-core/blob/master/Formula/g/git.rb"target="_blank" rel="noopener noreferrer"&gt;官方仓库&lt;/a&gt;下载历史版本。&lt;/p&gt;
&lt;p&gt;brew 脚本我认为是有 bug 的，它编译出的 git-credential-netrc 中模块路径错误，用的是编译时的临时目录，例如
&lt;code&gt;/private/tmp/git-20250527-25582-blyz61/git-2.49.0/.brew_home/share/perl5&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果不需要 git-credential-netrc，可以将相关脚本注释掉。或者参考方法 3 修改路径。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果想升级 git，可以将脚本中与 perl 相关部分注释掉，包括
git-credential-netrc、diff-highlight、git-send-email 相关代码，以及 Net::SMTP::SSL
模块（git-send-email 依赖它）。这也意味着这些工具将无法使用。&lt;/p&gt;
&lt;p&gt;git 源码中还有一个 &lt;code&gt;NO_PERL&lt;/code&gt; 环境变量，但我没试过。&lt;/p&gt;
&lt;p&gt;git-credential-netrc 可能用的人不多，毕竟 macOS 上一般用 SSH、git-credential-osxkeychain。但
diff-highlight 还是很有用的，可以在 diff 高亮显示不同的部分。git-send-email 用于发送邮件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果想升级 git 且需要 perl 相关工具，可以使用 brew 版 perl。&lt;/p&gt;
&lt;p&gt;在 brew 脚本中做如下修改：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/Formula/g/git.rb b/Formula/g/git.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index a58a53ea0d6..82200f7576f 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/Formula/g/git.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/Formula/g/git.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -23,6 +23,7 @@ class Git &amp;lt; Formula
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; depends_on &amp;#34;gettext&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; depends_on &amp;#34;pcre2&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ depends_on &amp;#34;perl&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; uses_from_macos &amp;#34;curl&amp;#34;, since: :catalina # macOS &amp;lt; 10.15.6 has broken cert path logic
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; uses_from_macos &amp;#34;expat&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -125,12 +126,13 @@ class Git &amp;lt; Formula
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # Generate diff-highlight perl script executable
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cd &amp;#34;contrib/diff-highlight&amp;#34; do
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- system &amp;#34;make&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ system &amp;#34;make&amp;#34;, &amp;#34;PERL_PATH=#{ENV[&amp;#34;PERL_PATH&amp;#34;]}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # Install the netrc credential helper
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cd &amp;#34;contrib/credential/netrc&amp;#34; do
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- system &amp;#34;make&amp;#34;, &amp;#34;test&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ system &amp;#34;make&amp;#34;, &amp;#34;PERL_PATH=#{ENV[&amp;#34;PERL_PATH&amp;#34;]}&amp;#34;, &amp;#34;test&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ inreplace &amp;#34;git-credential-netrc&amp;#34;, %r{(?&amp;lt;=\$ENV\{GITPERLLIB\} \|\| &amp;#39;)[^:&amp;#39;]+}, share/&amp;#34;perl5&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; git_core.install &amp;#34;git-credential-netrc&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;depends_on &amp;quot;perl&amp;quot;&lt;/code&gt; 指定 brew 版 perl，脚本会将路径保存在 &lt;code&gt;PERL_PATH&lt;/code&gt; 环境变量。&lt;/p&gt;
&lt;p&gt;子模块编译时此环境变量无效，因此 make 命令还是需要 &lt;code&gt;PERL_PATH=&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;inreplace&lt;/code&gt; 一行会将 git-credential-netrc 脚本中的临时路径&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-perl" data-lang="perl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;lib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/:/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GITPERLLIB&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/private/tmp/git-20250527-25582-blyz61/git-2.49.0/.brew_home/share/perl5:/Applications/Xcode.app/Contents/Developer/Library/Perl/5.40/darwin-thread-multi-2level:/Library/Developer/CommandLineTools/Library/Perl/5.40/darwin-thread-multi-2level&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;修改为形如 &lt;code&gt;/usr/local/Cellar/git/2.49.0/share/perl5&lt;/code&gt;，这也是编译脚本中模块安装位置。&lt;/p&gt;
&lt;p&gt;另外，临时路径之后的几个路径也是错误的。它在 brew 脚本里通过 &lt;code&gt;PERLLIB_EXTRA&lt;/code&gt; 设置，这可能是新版 macOS 的路径。但不影响使用。&lt;/p&gt;
&lt;p&gt;最后可将 &lt;code&gt;/usr/local/opt/git/share/git-core/contrib/diff-highlight&lt;/code&gt; 加入 &lt;code&gt;PATH&lt;/code&gt;
环境变量，并添加到 git 配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global pager.diff &lt;span class="s1"&gt;&amp;#39;diff-highlight | less&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global pager.log &lt;span class="s1"&gt;&amp;#39;diff-highlight | less&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global pager.show &lt;span class="s1"&gt;&amp;#39;diff-highlight | less&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="sha256-mismatch"&gt;sha256 mismatch&lt;/h2&gt;
&lt;p&gt;这类问题出现频率很高，在官方仓库可以看到大量相关的 issue、commit 和 PR。&lt;/p&gt;
&lt;p&gt;除了网络问题外，通常有两种原因。&lt;/p&gt;
&lt;h3 id="上游重新发布同一版本"&gt;上游重新发布同一版本&lt;/h3&gt;
&lt;p&gt;这时依旧修改脚本，例如修改 &lt;code&gt;launchcontrol&lt;/code&gt;，在 &lt;code&gt;$(brew --repository homebrew/cask)&lt;/code&gt; 目录下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/Casks/launchcontrol.rb b/Casks/launchcontrol.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index 5c2db44947..db25b721a8 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/Casks/launchcontrol.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/Casks/launchcontrol.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -1,7 +1,7 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cask &amp;#34;launchcontrol&amp;#34; do
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; on_catalina :or_older do
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; version &amp;#34;1.52.7&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;- sha256 &amp;#34;16c3d89e41a99cbf43e6996681358e8e7a4bc63fa770b9f8c0bc72c5356a0b8a&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ sha256 &amp;#34;760edc3f3238ecbbc9f0c14b17ced9ac2a46c46a4ed8feec6bfb532fced37b7e&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; livecheck do
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; skip &amp;#34;Legacy version&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;确认没问题的话可以去提 PR。&lt;/p&gt;
&lt;h3 id="源码归档在服务端重新生成"&gt;源码归档在服务端重新生成&lt;/h3&gt;
&lt;p&gt;GitHub 和 Gitlab 都使用 &lt;code&gt;git archive&lt;/code&gt; 将源码归档为 tar 包，但不管是 GitHub&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; 还是
Gitlab&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;，都不保证源码归档的稳定性。&lt;/p&gt;
&lt;p&gt;例如我发现的 Gitlab 校验变化问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 &lt;span class="p"&gt;|&lt;/span&gt; sha256sum
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2ddef549e1eaeecc1fc48f0d8332ea3545809e46509db69beb3a0a4bf19ef906 -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="c1"&gt;# 过一段时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 &lt;span class="p"&gt;|&lt;/span&gt; sha256sum
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0e988582f315fe76c909accf5e7f81b975c5bd2b850ee760d8e9fac297f70b5d -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;两个 tar 包内容完全一样，只有文件夹名不同：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 &amp;gt;old
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="c1"&gt;# 过一段时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ curl -fsSL https://gitlab.com/AOMediaCodec/SVT-AV1/-/archive/v1.4.1/SVT-AV1-v1.4.1.tar.bz2 &amp;gt;new
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tar xf old
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SVT-AV1-v1.4.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tar xf new
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SVT-AV1-018276d714ce65d9b586f6205ee016cbd8d5425d
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SVT-AV1-v1.4.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ diff SVT-AV1-v1.4.1 SVT-AV1-018276d714ce65d9b586f6205ee016cbd8d5425d -r
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我也是服了某些 brew 的维护者，复现不了我的问题就说是我配置问题，神他妈配置问题😅&lt;/p&gt;
&lt;p&gt;Gitlab 官方人员解释如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The root cause was discussed in a confidential issue, but I can share a
general idea what went wrong.&lt;/p&gt;
&lt;p&gt;One of deployed fixes had a side-effect of changing the hash sum of the
archived files. When it was discovered, we reverted the code. However,
Cloudflare still responded with old archive files for some regions. We updated
the cache and that resolved the problem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;好了，破案了。Gitlab 也是个鬼才，归档生成机制变了也就算了，同一个老 URL，还能返回不同的文件。&lt;/p&gt;
&lt;p&gt;要是不幸遇到这种问题，自认倒霉吧，依旧直接改脚本。鬼知道什么时候抽风。&lt;/p&gt;
&lt;p&gt;其结果就是，明明服务商不保证自动归档的稳定性，而 brew 却依赖这一点。&lt;/p&gt;
&lt;p&gt;要保证源码包的稳定性，应该由作者官方以 release 形式发布，就像各种开源软件官网那样，然后由官方提供校验方法。&lt;/p&gt;
&lt;p&gt;那么从全局看，怎么避免这种错误？我觉得 brew 完全可以定期检查哈希值，如果变更则自动提 PR，这一点可以通过 CI/CD 自动化完成。&lt;/p&gt;
&lt;h2 id="warning-you-are-using-macos-1015"&gt;Warning: You are using macOS 10.15.&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;We (and Apple) do not provide support for this old version.
It is expected behaviour that some formulae will fail to build in this old version.
It is expected behaviour that Homebrew will be buggy and slow.
Do not create any issues about this on Homebrew&amp;#39;s GitHub repositories.
Do not create any issues even if you think this message is unrelated.
Any opened issues will be immediately closed without response.
Do not ask for help from Homebrew or its maintainers on social media.
You may ask for help in Homebrew&amp;#39;s discussions but are unlikely to receive a response.
Try to figure out the problem yourself and submit a fix as a pull request.
We will review it but may or may not accept it.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;啊对对对，我知道我们老古董不配用高贵的 brew，出了问题后果自负我理解。&lt;/p&gt;
&lt;p&gt;但是你每次一大堆烦人的警告跳脸连个他妈的开关都没有？哪怕减到一行呢？&lt;/p&gt;
&lt;p&gt;何况苹果只是对系统停止了支持，你一个包管理器，不想支持老系统直接大方说不就行了，和苹果对系统的支持有个毛线关系？难道苹果不让 Catalina 用户用 App
Store 了？Ubuntu 就连 12.04 甚至更老的版本，改一下源就能继续用官方的老仓库。&lt;/p&gt;
&lt;p&gt;对于一个非盈利的开源软件，我表示支持，但你别又当又立😅&lt;/p&gt;
&lt;p&gt;发现我脾气逐渐暴躁，管他的，反正鬼佬看不懂中文。&lt;/p&gt;
&lt;p&gt;编辑 &lt;code&gt;$(brew --repository)/Library/Homebrew/extend/os/mac/diagnostic.rb&lt;/code&gt; 这个脚本，在
&lt;code&gt;check_for_unsupported_macos&lt;/code&gt; 方法第一行直接返回：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;index 1cbc907f3..37b5b91ba 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gd"&gt;--- a/Library/Homebrew/extend/os/mac/diagnostic.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+++ b/Library/Homebrew/extend/os/mac/diagnostic.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;@@ -106,6 +106,7 @@ module Homebrew
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; def check_for_unsupported_macos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gi"&gt;+ return
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return if Homebrew::EnvConfig.developer?
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return if ENV[&amp;#34;HOMEBREW_INTEGRATION_TEST&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;整个世界都清净了！&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://github.com/openssl/openssl/issues/27950#issuecomment-3032659229"target="_blank" rel="noopener noreferrer"&gt;https://github.com/openssl/openssl/issues/27950#issuecomment-3032659229&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://github.blog/2023-02-21-update-on-the-future-stability-of-source-code-archives-and-hashes/"target="_blank" rel="noopener noreferrer"&gt;https://github.blog/2023-02-21-update-on-the-future-stability-of-source-code-archives-and-hashes/&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://gitlab.com/gitlab-org/gitlab/-/issues/402616"target="_blank" rel="noopener noreferrer"&gt;https://gitlab.com/gitlab-org/gitlab/-/issues/402616&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>在 Android 上部署 Linux</title><link>https://binac.org/posts/deploying-linux-on-android/</link><pubDate>Mon, 27 Mar 2023 03:57:00 +0800</pubDate><guid>https://binac.org/posts/deploying-linux-on-android/</guid><description>&lt;p&gt;在 Android 上部署 Linux……好吧，我知道 Android 本身就是个 Linux。不要在意这些细节。&lt;/p&gt;
&lt;p&gt;最近把旧手机当时钟用，发现它还有 TF 卡槽，想起何不在上面跑个 Linux，化身低功耗服务器呢，再搭个 Syncthing 私人云盘，岂不美哉（摊手）？&lt;/p&gt;
&lt;p&gt;为啥不直接用 Syncthing APP？&lt;del&gt;呃，你不觉得这样很酷吗？我觉得太酷了……&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;del&gt;为了物尽其用，又买了个 TF 卡……&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;长话短说，就是用
&lt;a href="https://github.com/meefik/linuxdeploy"target="_blank" rel="noopener noreferrer"&gt;Linux Deploy&lt;/a&gt;，它能以 chroot 方式在 Android 上创建 Linux 环境。和 Termux 相比，它更像一个完整的 Linux。&lt;/p&gt;
&lt;p&gt;其实很早就用过 LP，不过 LP 看起来有段时间没更新了，一大堆 issue 没人理。新手要踩的坑还是很多的，特此记录。&lt;/p&gt;
&lt;h2 id="安装"&gt;安装&lt;/h2&gt;
&lt;p&gt;首先，用 &lt;a href="https://github.com/topjohnwu/Magisk"target="_blank" rel="noopener noreferrer"&gt;Magisk&lt;/a&gt;
把手机 root 了。Android 版本太老的话是没法使用新版 Magisk
APP 的，在 release 页找老版 Magisk。&lt;/p&gt;
&lt;p&gt;其次，把 VPN 之类的关掉（装完再关也可以），那玩意会在手机上建 NAT，局域网设备就没法通过 IP 地址访问了。鬼知道我在这个奇葩问题上花了多少时间……&lt;/p&gt;
&lt;p&gt;如果要爬梯，可以用这个
&lt;a href="https://github.com/yatsuki/v2ray"target="_blank" rel="noopener noreferrer"&gt;V2Ray Magisk 模块&lt;/a&gt;，它能在手机建立透明代理。虽然版本很老，也有一些 bug，但确实能用。&lt;/p&gt;
&lt;p&gt;打开 LP，右下角选项，发行版就用 Debian。并不是所有发行版都能用，既然 LP 默认 Debian，意味着它的问题可能是最少的。目前 Debian 最新只能用 buster。&lt;/p&gt;
&lt;p&gt;架构就按自己手机来。&lt;/p&gt;
&lt;p&gt;官方的源经常访问故障，建议使用国内镜像服务，比如清华源
&lt;code&gt;http://mirrors.tuna.tsinghua.edu.cn/debian/&lt;/code&gt;，注意是 &lt;code&gt;http&lt;/code&gt; 而不是 &lt;code&gt;https&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;安装类型，我是直接把环境安装到 TF 卡，所以选择分区，而不是镜像文件或目录。这样不仅保持 Linux 环境和 Android 相互独立，而且读写性能比镜像文件更好。&lt;/p&gt;
&lt;p&gt;具体做法是，先把 TF 卡分区为单个 ext4，当然如果你知道你在做什么，也可以建立其它分区，只要下面的安装路径正确即可。&lt;/p&gt;
&lt;p&gt;插入 TF 卡，万恶的 Android 会自动挂载，然后在里面拉屎。先不要管它，在设置里把 TF 卡弹出。&lt;/p&gt;
&lt;p&gt;安装路径一般为
&lt;code&gt;/dev/block/mmcblk1p1&lt;/code&gt;，根据自己的来，可以使用终端、文件管理器等查看。&lt;/p&gt;
&lt;p&gt;初始化系统使用 SysV。chroot 环境是不能用 systemd 的。不建议尝试那些模拟 systemd 的替代品，SysV 和 cron 就可以解决大部分问题。&lt;/p&gt;
&lt;p&gt;启用 ssh，不要 GUI。&lt;/p&gt;
&lt;p&gt;其它的按自己的喜好来。&lt;/p&gt;
&lt;p&gt;安装只要十几分钟就搞定了，如果出现问题，到 LP 设置里把调试打开看看 log。&lt;/p&gt;
&lt;p&gt;然后启动就能用 ssh 访问了。&lt;/p&gt;
&lt;p&gt;另外，由于 Android 省电机制，关闭屏幕后会非常卡，除非保持在 LP 主界面，或安装禁止关屏的 APP。LP 设置里有保持 Wi-Fi 和 CPU 的选项，但用处不大，至少在高版本 Android 是不管用的。&lt;/p&gt;
&lt;h2 id="天坑"&gt;天坑&lt;/h2&gt;
&lt;h3 id="etcrclocal"&gt;/etc/rc.local&lt;/h3&gt;
&lt;p&gt;虽然有 SysV，但很多软件都转用 systemd了。&lt;/p&gt;
&lt;p&gt;这时肯定想到 &lt;code&gt;/etc/rc.local&lt;/code&gt; 开机脚本，然而它也缺失了。啊这……&lt;/p&gt;
&lt;p&gt;所以我们需要手动建立 &lt;code&gt;/etc/init.d/rc.local&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#! /bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;### BEGIN INIT INFO&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Provides: rc.local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Required-Start: $all&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Required-Stop:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Default-Start: 2 3 4 5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Default-Stop:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Short-Description: Run /etc/rc.local if it exist&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;### END INIT INFO&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# https://forums.raspberrypi.com/viewtopic.php?t=95101&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/sbin:/usr/sbin:/bin:/usr/bin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;. /lib/init/vars.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;. /lib/lsb/init-functions
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;do_start&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -x /etc/rc.local &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VERBOSE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; no &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; log_begin_msg &lt;span class="s2"&gt;&amp;#34;Running local boot scripts (/etc/rc.local)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /etc/rc.local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;ES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VERBOSE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; no &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; log_end_msg &lt;span class="nv"&gt;$ES&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$ES&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; start&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; do_start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; restart&lt;span class="p"&gt;|&lt;/span&gt;reload&lt;span class="p"&gt;|&lt;/span&gt;force-reload&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Error: argument &amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39; not supported&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; stop&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; start|stop&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以及 &lt;code&gt;/etc/rc.local&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/sh -e
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# rc.local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# This script is executed at the end of each multiuser runlevel.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Make sure that the script will &amp;#34;exit 0&amp;#34; on success or any other&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# value on error.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# In order to enable or disable this script just change the execution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# bits.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# By default this script does nothing.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp /etc/hostname /proc/sys/kernel/hostname
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf /Android
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod &lt;span class="m"&gt;666&lt;/span&gt; /dev/fuse
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;给它们加上可执行权限，然后用 &lt;code&gt;sysv-rc-conf&lt;/code&gt; 或者其它你喜欢的工具启用。&lt;/p&gt;
&lt;p&gt;看看我已经在 &lt;code&gt;rc.local&lt;/code&gt; 里加了什么内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/etc/hostname&lt;/code&gt; 中的内容不会被读取，所以这里把它复制一下。&lt;/li&gt;
&lt;li&gt;Android 开机就会自动挂载 TF 卡并在里面拉屎，所以把屎目录 &lt;code&gt;Android&lt;/code&gt;
删掉。而且挂载之后，LP 就没法启动了，这个坑之后再说，如何在开机时取消挂载。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/fuse&lt;/code&gt; 由于权限限制需要修改一下，否则无法使用 rclone 等工具。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TNND，这 TM 都是坑啊。&lt;/p&gt;
&lt;h3 id="关闭-rsyslog"&gt;关闭 rsyslog&lt;/h3&gt;
&lt;p&gt;Android 的 loglevel 开到了 7，那不是一般的多：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# cat /proc/sys/kernel/printk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;7 4 1 &lt;span class="m"&gt;7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;而且手动去改也是没用的，估计是因为全被 logcat 接管了。&lt;/p&gt;
&lt;p&gt;LP 启动没多久，&lt;code&gt;/var/log&lt;/code&gt; 大小就按 GiB 来计算了，非常坑爹，嫌闪存长寿啊。&lt;/p&gt;
&lt;p&gt;也考虑过使用 zram 分区，但自带的 logrotate 似乎有 bug，配置不生效，而且不知道 zram 在 chroot 下能否正常使用，等有空再折腾吧。&lt;/p&gt;
&lt;p&gt;最后干脆直接用 &lt;code&gt;sysv-rc-conf&lt;/code&gt; 关闭了 rsyslog。&lt;/p&gt;
&lt;p&gt;清净了！&lt;/p&gt;
&lt;h3 id="开机取消挂载-tf-卡"&gt;开机取消挂载 TF 卡&lt;/h3&gt;
&lt;p&gt;之前已经把环境安装到 TF 卡了，如果想让 LP 开机启动，就不能让系统挂载 TF 卡。&lt;/p&gt;
&lt;p&gt;我研究了一下，开机禁止挂载比较麻烦，除非修改源码，但开机自动挂载后再手动取消挂载（弹出）就容易得多。&lt;/p&gt;
&lt;p&gt;于是我写了个 Magisk 开机脚本：&lt;/p&gt;
&lt;script src="https://gist.github.com/qianbinbin/e93e19940dda264cc5cd2ebdddbe565e.js?file=unmount.sh"&gt;&lt;/script&gt;
&lt;p&gt;它会弹出所有外置存储，比如 TF 卡、U 盘等。&lt;/p&gt;
&lt;p&gt;由于脚本里使用了 &lt;code&gt;sm&lt;/code&gt; 工具，需要 Android 6.0 以上才能用。&lt;/p&gt;
&lt;p&gt;把脚本放到 &lt;code&gt;/data/adb/service.d/&lt;/code&gt;
下重启，看看是不是一挂载就马上弹出了。如果不成功，把 &lt;code&gt;LOG_ON=false&lt;/code&gt; 改为
&lt;code&gt;LOG_ON=true&lt;/code&gt; ，查看 &lt;code&gt;/data/local/tmp/unmount.log&lt;/code&gt; 排查一下原因。&lt;/p&gt;
&lt;p&gt;我自己测试，一般在开机后 20 多秒就会弹出，比 LP 启动快。如果弹出太慢，建议把 LP 启动延迟改大一些。&lt;/p&gt;
&lt;h2 id="服务"&gt;服务&lt;/h2&gt;
&lt;h3 id="syncthing"&gt;Syncthing&lt;/h3&gt;
&lt;p&gt;使用 &lt;a href="https://apt.syncthing.net/"target="_blank" rel="noopener noreferrer"&gt;官方仓库&lt;/a&gt; 安装。配置略。&lt;/p&gt;
&lt;h3 id="rclone"&gt;rclone&lt;/h3&gt;
&lt;p&gt;参考我的文章：&lt;a href="https://binac.org/posts/managing-cloud-storage-with-rclone-and-openlist/"&gt;使用 rclone 和 openlist 管理云存储&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="nginx"&gt;Nginx&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://nginx.org/en/linux_packages.html#Debian"target="_blank" rel="noopener noreferrer"&gt;Nginx 官方仓库&lt;/a&gt;里只有 arm64 和 amd64，32 位的手机就用默认仓库里的老版本吧。&lt;/p&gt;
&lt;p&gt;Nginx 一大好处是给搭建反向代理，比如：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;location /your-syncthing-path/ {
proxy_pass https://localhost:8384/;
...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Transmission、aria2 等下载工具都可以这么干，对外隐藏真实端口，更重要的是可以走 HTTPS。今天在网上用 HTTP 和裸奔有什么区别？&lt;/p&gt;
&lt;p&gt;好了，再填一个大坑，给你节省半天时间：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;由于 Android 权限限制，必须把 &lt;code&gt;www-data&lt;/code&gt; 加入 &lt;code&gt;aid_inet&lt;/code&gt; 用户组：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usermod -aG aid_inet www-data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;否则没法使用。所以 Android 真不能当作普通的 Linux 发行版……&lt;/p&gt;
&lt;h3 id="certbot"&gt;certbot&lt;/h3&gt;
&lt;p&gt;certbot 用 apt 安装即可，版本虽老但够用。用 pip 安装的话会给你装 20 几个依赖，人麻了……apt 虽然也有，但起码可以
&lt;code&gt;autoremove&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;国内 ISP 屏蔽了普通用户的 80 端口，要申请证书可以参考&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-acquire-a-let-s-encrypt-certificate-using-dns-validation-with-acme-dns-certbot-on-ubuntu-18-04"target="_blank" rel="noopener noreferrer"&gt;这篇文章&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;另外，certbot 是没有 SysV 脚本的，要自动更新可以直接添加到 cron，比如每天更新两次
&lt;code&gt;0 0,12 * * * /usr/bin/certbot -q renew&lt;/code&gt;，跟 systemd 脚本是一样的效果。&lt;/p&gt;
&lt;h2 id="其它"&gt;其它&lt;/h2&gt;
&lt;h3 id="永久开启无线调试"&gt;永久开启无线调试&lt;/h3&gt;
&lt;p&gt;打开终端或 &lt;code&gt;adb shell&lt;/code&gt;，使用 &lt;code&gt;su&lt;/code&gt; 获取 root 权限后输入：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;setprop persist.adb.tcp.port &lt;span class="m"&gt;5555&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;重启以后也会保持开启。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意切不可在公网上开启！Android 漏洞极多，尤其是老旧版本，非常容易成为肉鸡！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="关闭旋转建议按钮"&gt;关闭旋转建议按钮&lt;/h3&gt;
&lt;p&gt;我就好奇什么样的低能玩意设计出了这么个脑瘫功能？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb shell settings put secure show_rotation_suggestions &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="充电控制"&gt;充电控制&lt;/h3&gt;
&lt;p&gt;据说手机常年通电，电池容易鼓包，有搞硬件的大佬会把电池去掉，让主板直接从 USB 取电，我是不想折腾，总有种因噎废食的感觉，万一断电了呢？硬件损伤且不说，数据丢失就很难受。&lt;/p&gt;
&lt;p&gt;其实，一个
&lt;a href="https://github.com/VR-25/acc"target="_blank" rel="noopener noreferrer"&gt;ACC Magisk 模块&lt;/a&gt;就可以搞定，安装完直接重启，默认配置就够用了。它会让手机电量保持在 70
~ 75，电量低了就充电，高了就断电，省心得很。&lt;/p&gt;
&lt;h3 id="f-droid-特权扩展"&gt;F-Droid 特权扩展&lt;/h3&gt;
&lt;p&gt;F-Droid 不能自动安装 APP，root 也不行。官方有一个&lt;a href="https://f-droid.org/en/packages/org.fdroid.fdroid.privileged.ota/"target="_blank" rel="noopener noreferrer"&gt;特权扩展 OTA 包&lt;/a&gt;，可以通过 recovery 刷入。但这会修改 system 分区，我不喜欢。&lt;/p&gt;
&lt;p&gt;Magisk 官方模块仓库（已废弃）倒是有一个模块，但一来版本太老，二来写得不咋地，三来作者是个 GD……真晦气。&lt;/p&gt;
&lt;p&gt;于是我大体看了一下 Magisk 官方文档，自己写了个模块：&lt;a href="https://github.com/qianbinbin/fdroid-priv-ext"target="_blank" rel="noopener noreferrer"&gt;F-Droid 特权扩展&lt;/a&gt;，通过 GitHub
Actions 自动构建，与 F-Droid 官方保持同步。&lt;/p&gt;
&lt;p&gt;需要注意的是，&lt;strong&gt;安装模块后 F-Droid 网络访问权限可能会丢失&lt;/strong&gt;，要重新设置一下。这也是个大坑，因为没有任何有用的提示，你会一直怀疑是自己网络问题，然后浪费几个小时调试。&lt;/p&gt;
&lt;h3 id="远程控制"&gt;远程控制&lt;/h3&gt;
&lt;p&gt;由于是作为服务器用的，远程控制还是很有用的。&lt;/p&gt;
&lt;p&gt;我试过很多 VNC 软件，都不靠谱。&lt;/p&gt;
&lt;p&gt;最后发现了神器：&lt;a href="https://www.coolapk.com/apk/com.didjdk.adbhelper"target="_blank" rel="noopener noreferrer"&gt;甲壳虫&lt;/a&gt;，它基于
&lt;a href="https://github.com/Genymobile/scrcpy"target="_blank" rel="noopener noreferrer"&gt;scrcpy&lt;/a&gt;，通过 adb 控制手机。结合前面永久开启无线调试，用起来十分方便。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;如果有公网 IP 或设置好内网穿透，就可以实现字面意义上的远程控制——只要延迟能忍的话。&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意切不可在公网上开启！Android 漏洞极多，尤其是老旧版本，非常容易成为肉鸡！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;遗憾的是它只支持标准的 5555 端口，不能自定义，也不支持路径（反向代理）。&lt;/p&gt;
&lt;h3 id="时钟"&gt;时钟&lt;/h3&gt;
&lt;p&gt;可能是我孤陋寡闻，Android APP 浩如烟海，我竟然找不到一款简洁好用的待机时钟。&lt;/p&gt;
&lt;p&gt;目前用的这个名字就不说了，CPU 占用率高得离谱。&lt;/p&gt;
&lt;p&gt;有空自己写一个。&lt;/p&gt;</description></item></channel></rss>