[{"content":"在支持 Touch ID、且已录入指纹的 Mac 上，可以通过 PAM 的 pam_tid.so 模块为 sudo 增加指纹验证。下面两种方法选择一种执行。\n一键修改 sudo 配置 在 macOS 的 Zsh 或 Bash 终端中执行：\n1 sudo sed -i \u0026#34;.bak\u0026#34; \u0026#39;2s/^/auth sufficient pam_tid.so\\\u0026#39;$\u0026#39;\\n/g\u0026#39; /etc/pam.d/sudo 命令使用 macOS 自带的 BSD sed，在 /etc/pam.d/sudo 第二行前插入：\n1 auth sufficient pam_tid.so 原配置自动备份为 /etc/pam.d/sudo.bak。$'\\n' 由 Zsh/Bash 转换为换行，不能直接照搬到不支持这种语法的 Shell。\n此命令适用于尚未添加该规则的配置；重复执行会重复插入，并更新备份。直接修改的系统文件可能在 macOS 更新时被替换。\n可选：使用系统的 sudo_local 配置 如果 /etc/pam.d/sudo 包含 auth include sudo_local，且系统提供 /etc/pam.d/sudo_local.template，可以使用下面的一键方法。系统模板将 sudo_local 定义为供本地配置使用、在系统更新后保留的文件：\n1 sudo sh -c \u0026#39;test -e /etc/pam.d/sudo_local || cp /etc/pam.d/sudo_local.template /etc/pam.d/sudo_local\u0026#39; \u0026amp;\u0026amp; sudo sed -i \u0026#34;.bak\u0026#34; \u0026#39;s/^#\\(auth[[:space:]]*sufficient[[:space:]]*pam_tid\\.so\\)/\\1/\u0026#39; /etc/pam.d/sudo_local 命令在 sudo_local 不存在时复制系统模板，然后自动取消模板中 Touch ID 规则的注释，备份为 /etc/pam.d/sudo_local.bak。已有文件会保留；该方法要求文件中包含模板的对应规则。\n验证效果 1 2 sudo -k sudo whoami sudo -k 清除已有的授权缓存，再次执行时可测试指纹验证，成功后输出 root。Touch ID 需要本机支持的交互环境，SSH 等会话不能直接等同于本机终端的指纹验证。\n配置依据：macOS 系统文件 /etc/pam.d/sudo 与 /etc/pam.d/sudo_local.template。\n","date":"2026-10-10T00:00:00+08:00","permalink":"/posts/macos-sudo-touch-id/","title":"macOS 一键开启 sudo 的 Touch ID 验证"},{"content":"Windows 下载文件时可能记录 Zone.Identifier 来源信息。在部分 WSL 或共享目录场景中，这些信息会以 文件名:Zone.Identifier 的普通文件形式出现，可以通过 find 批量清理。\n一键清理 进入需要清理的目录后执行：\n1 find . -name \u0026#34;*Zone.Identifier\u0026#34; -type f -delete 命令从当前目录 . 开始，递归查找所有子目录，直接删除文件名以 Zone.Identifier 结尾的普通文件。双引号让通配符由 find 匹配；-type f 限定普通文件，避免删除同名目录。\n例如，example.zip:Zone.Identifier 会被删除，example.zip 保留。匹配按文件名区分大小写，也不会匹配 example.zip:Zone.Identifier.backup。\n可选：先查看匹配结果 需要查看范围时，可以在删除前执行：\n1 find . -name \u0026#34;*Zone.Identifier\u0026#34; -type f -print 此方法清理的是目录中实际存在的普通文件，适用于 Linux 的 GNU find，也可用于支持这些参数的 macOS find。Windows NTFS 中附着于文件的备用数据流不属于这种普通文件，不能将本命令理解为通用的 Windows 文件解锁命令。\n参考：Microsoft 的 Zone.Identifier 说明、共享存储中的 Zone.Identifier 文件、GNU find 清理文件。\n","date":"2026-10-10T00:00:00+08:00","permalink":"/posts/linux-delete-zone-identifier/","title":"Linux 一键清理 Zone.Identifier 文件"},{"content":"通过网站响应头中的 Date 获取时间，再用 date -s 校准系统时间。适用于安装了 curl、awk、tr 和 GNU date 的 Linux 环境，需要能够正常访问这些 HTTPS 站点，并以 root 身份执行。\n一键校时 1 TIME=\u0026#34;$(for u in https://www.baidu.com https://www.qq.com https://www.aliyun.com https://www.cloudflare.com; do t=\u0026#34;$(curl -fsSI --connect-timeout 5 \u0026#34;$u\u0026#34; 2\u0026gt;/dev/null | awk -F\u0026#39;: \u0026#39; \u0026#39;tolower($1)==\u0026#34;date\u0026#34;{print $2; exit}\u0026#39; | tr -d \u0026#39;\\r\u0026#39;)\u0026#34;; [ -n \u0026#34;$t\u0026#34; ] \u0026amp;\u0026amp; printf \u0026#39;%s\\n\u0026#39; \u0026#34;$t\u0026#34; \u0026amp;\u0026amp; break; done)\u0026#34;; [ -n \u0026#34;$TIME\u0026#34; ] \u0026amp;\u0026amp; date -s \u0026#34;$TIME\u0026#34; 命令依次尝试百度、腾讯、阿里云和 Cloudflare。每个站点的连接超时为 5 秒，取得非空的 Date 后停止尝试；没有取得时间时不会执行 date -s。\nDate 中带有 GMT 时区，GNU date 会据此设置系统时间。显示结果使用系统当前时区，此命令不会修改时区。\n查看结果 1 date 这是一次性校时，精度受网站响应时间和网络延迟影响，不会开启持续时间同步服务。macOS 的系统 date 和部分 BusyBox date 参数、解析能力不同，不能直接照搬此命令。\n参考：curl 参数说明、GNU date 用法。\n","date":"2026-10-10T00:00:00+08:00","permalink":"/posts/linux-sync-time-http/","title":"Linux 通过 HTTP 响应头一键校准系统时间"},{"content":"青龙是支持 Python3、JavaScript、Shell 和 TypeScript 的定时任务管理平台，可以通过网页管理脚本、环境变量和任务日志。\n一键部署 1 2 3 4 5 6 docker run -dit \\ -v ~/ql/data:/ql/data \\ -p 5700:5700 \\ --name qinglong \\ --restart unless-stopped \\ whyour/qinglong:latest 部署后访问 http://IP:5700/，其中 IP 替换为 Docker 宿主机地址。\n参数说明 参数 作用 -v ~/ql/data:/ql/data 将容器数据保存在当前用户的 ~/ql/data 目录 -p 5700:5700 将面板网页端口映射到宿主机 --name qinglong 容器名称 --restart unless-stopped 容器退出时自动重启，手动停止后保持停止状态 whyour/qinglong:latest 使用官方 latest 镜像 参考：青龙项目、青龙文档。\n延伸阅读 Linux 安装 Docker Engine 与 Docker Compose ","date":"2026-10-10T00:00:00+08:00","permalink":"/posts/docker-qinglong/","title":"Docker 一键部署青龙面板"},{"content":"通过 Samba 将 Docker 宿主机的 /samba 目录共享给 Windows、macOS 等 SMB 客户端，共享名称为 www。\n一键部署 1 docker run -d --name samba --restart=unless-stopped -p 139:139 -p 445:445 -v /samba:/mount dperson/samba -u \u0026#34;www;www\u0026#34; -s \u0026#34;www;/mount;yes;no;no;all;none\u0026#34; /samba 是宿主机目录，/mount 是对应的容器目录。需要共享其他位置时，只替换挂载参数中冒号左侧的 /samba 即可；宿主机的 TCP 139 和 445 端口需可供容器使用。\n连接共享 客户端 访问方式 Windows 在文件资源管理器地址栏输入 \\\\IP\\www macOS Finder 中按 Command + K，输入 smb://IP/www IP 替换为 Docker 宿主机地址。登录用户名为 www，密码为 www；需要其他账号或密码时，修改 -u \u0026quot;用户名;密码\u0026quot;。\n共享参数说明 -s 的各字段以分号分隔，本例依次为：\n字段 值 含义 共享名称 www 客户端访问的共享名 共享目录 /mount 容器内的共享路径 可浏览 yes 允许浏览该共享 只读 no 共享配置允许写入 访客访问 no 要求使用账号认证 允许的用户 all 不限定到某个用户名，允许通过认证的用户访问 管理员用户 none 不指定共享管理员用户 all 不会覆盖前面的访客设置，本例仍需登录。实际写入还取决于共享目录的文件系统权限；镜像提供的 -p 参数可自动设置共享目录的属主和权限，需要时加在 dperson/samba 后面，它会修改目录内文件的权限。\n参考：dperson/samba 部署与参数说明。\n延伸阅读 Linux 安装 Docker Engine 与 Docker Compose Linux 将目录权限设为 755、文件权限设为 644 ","date":"2026-10-10T00:00:00+08:00","permalink":"/posts/docker-samba/","title":"Docker 一键部署 Samba 文件共享"},{"content":"Runo 是基于 Bruno 的自托管 API 客户端，通过浏览器管理和发送请求，请求由部署 Runo 的服务器执行。\n一键部署 1 docker run -d --restart=unless-stopped --name runo -p 9700:9700 -e RUNO_DISABLE_AUTH=1 ghcr.io/takovibe/runo:latest 部署后访问 http://IP:9700/，其中 IP 替换为 Docker 宿主机地址。\n参数说明 参数 作用 --name runo 容器名称 -p 9700:9700 将 Runo 的网页端口映射到宿主机 RUNO_DISABLE_AUTH=1 强制免登录模式，访问者可直接使用服务器发送 API 请求 --restart=unless-stopped 容器退出时自动重启，手动停止后保持停止状态 此命令没有指定持久化挂载。需要保存配置、工作区和请求集合时，可以在镜像名称前加上 -v runo-data:/data，将数据保存在 Docker 数据卷中。\n参考：Runo 项目与配置说明。\n延伸阅读 Linux 安装 Docker Engine 与 Docker Compose Docker 部署 Hoppscotch 前端 ","date":"2026-10-10T00:00:00+08:00","permalink":"/posts/docker-runo/","title":"Docker 一键部署 Runo API 客户端"},{"content":"本文与 Ookla Speedtest CLI 都安装到 /usr/local/bin/speedtest。两者是不同的测速工具，先后安装时，后安装的会覆盖该路径下已有的程序。\nMac一键安装iNetSpeed-CLI 1 2 3 4 5 sudo mkdir -p /usr/local/bin \\ \u0026amp;\u0026amp; curl -fsSL \u0026#34;https://github.com/nxtrace/iNetSpeed-CLI/releases/latest/download/speedtest-darwin-$(uname -m | sed \u0026#39;s/x86_64/amd64/\u0026#39;).tar.gz\u0026#34; \\ | sudo tar -xz -C /usr/local/bin speedtest \\ \u0026amp;\u0026amp; sudo xattr -rc /usr/local/bin/speedtest \\ \u0026amp;\u0026amp; sudo chmod +x /usr/local/bin/speedtest Linux一键安装iNetSpeed-CLI 1 2 3 4 sudo mkdir -p /usr/local/bin \\ \u0026amp;\u0026amp; curl -fsSL \u0026#34;https://github.com/nxtrace/iNetSpeed-CLI/releases/latest/download/speedtest-linux-$(uname -m | sed \u0026#39;s/x86_64/amd64/;s/aarch64/arm64/\u0026#39;).tar.gz\u0026#34; \\ | sudo tar -xz -C /usr/local/bin speedtest \\ \u0026amp;\u0026amp; sudo chmod +x /usr/local/bin/speedtest 验证安装 1 speedtest -v 如果有输出版本号就说明安装成功了。\n延伸阅读 macOS 安装 Ookla Speedtest CLI macOS 与 Linux 安装 NextTrace ","date":"2026-09-27T00:00:00Z","permalink":"/posts/inetspeed-install/","title":"macOS 与 Linux 安装 iNetSpeed CLI"},{"content":"下载鼠须管 稳定版和每晚构建版二选一即可。\n稳定版 自动获取最新正式版：\n1 2 3 4 5 curl -fsSL https://api.github.com/repos/rime/squirrel/releases/latest \\ | grep \u0026#39;\u0026#34;browser_download_url\u0026#34;:\u0026#39; \\ | grep \u0026#39;\\.pkg\u0026#34;\u0026#39; \\ | cut -d \u0026#39;\u0026#34;\u0026#39; -f 4 \\ | xargs curl -fL -o /tmp/Squirrel.pkg 每晚构建版 自动获取最新 Nightly Build：\n1 2 3 4 5 curl -fsSL https://api.github.com/repos/rime/squirrel/releases/tags/latest \\ | grep \u0026#39;\u0026#34;browser_download_url\u0026#34;:\u0026#39; \\ | grep \u0026#39;\\.pkg\u0026#34;\u0026#39; \\ | cut -d \u0026#39;\u0026#34;\u0026#39; -f 4 \\ | xargs curl -fL -o /tmp/Squirrel.pkg 正式版中的 releases/latest 是 GitHub 获取最新正式 Release 的接口； 每晚构建版中的 tags/latest 是获取 Squirrel 名为 latest 的 Nightly Release。\n解包鼠须管 不使用 macOS installer 安装 .pkg，直接将安装包展开：\n1 2 rm -rf /tmp/Squirrel pkgutil --expand-full /tmp/Squirrel.pkg /tmp/Squirrel 安装鼠须管 删除已经存在的鼠须管：\n1 sudo rm -rf \u0026#34;/Library/Input Methods/Squirrel.app\u0026#34; 将解包得到的 Squirrel.app 移动到 macOS 输入法目录：\n1 2 3 sudo mv \\ \u0026#34;$(find /tmp/Squirrel -name \u0026#34;Squirrel.app\u0026#34; -type d -print -quit)\u0026#34; \\ \u0026#34;/Library/Input Methods/Squirrel.app\u0026#34; 修正应用所有者和用户组：\n1 sudo chown -R root:wheel \u0026#34;/Library/Input Methods/Squirrel.app\u0026#34; 注册鼠须管 注册到 macOS 输入法系统：\n1 sudo \u0026#34;/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel\u0026#34; --register-input-source 构建鼠须管共享数据 进入鼠须管共享数据目录并执行构建：\n1 2 3 cd \u0026#34;/Library/Input Methods/Squirrel.app/Contents/SharedSupport\u0026#34; \\ \u0026amp;\u0026amp; \u0026#34;/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel\u0026#34; --build \\ \u0026amp;\u0026amp; cd 启用鼠须管 启用鼠须管输入法：\n1 \u0026#34;/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel\u0026#34; --enable-input-source 切换到鼠须管：\n1 \u0026#34;/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel\u0026#34; --select-input-source 下载雾凇拼音 直接下载雾凇拼音官方 Nightly Release 的完整配置包：\n1 2 3 curl -fL \\ \u0026#34;https://github.com/iDvel/rime-ice/releases/download/nightly/full.zip\u0026#34; \\ -o /tmp/rime-ice.zip 解压雾凇拼音 1 2 3 rm -rf /tmp/rime-ice mkdir -p /tmp/rime-ice unzip -q /tmp/rime-ice.zip -d /tmp/rime-ice 安装雾凇拼音 全新安装时先清空鼠须管用户配置目录：\n1 2 3 rm -rf ~/Library/Rime mkdir -p ~/Library/Rime mv /tmp/rime-ice/* ~/Library/Rime 安装Blue Reverie主题 下载 Blue Reverie 的鼠须管主题配置：\n1 2 3 curl -fL \\ \u0026#34;https://raw.githubusercontent.com/hunter-ji/blue-reverie-rime-theme/main/squirrel.custom.yaml\u0026#34; \\ -o ~/Library/Rime/squirrel.custom.yaml 主题配置会以 squirrel.custom.yaml 的形式覆盖鼠须管默认外观配置，不需要修改雾凇拼音本身的配置文件。\n设置候选词数量和Caps Lock行为 创建 default.custom.yaml，将每页候选词数量设置为 9：\n1 2 3 4 5 cat \u0026gt; ~/Library/Rime/default.custom.yaml \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; patch: \u0026#34;menu/page_size\u0026#34;: 9 \u0026#34;ascii_composer/switch_key/Caps_Lock\u0026#34;: commit_code EOF 其中：\nmenu/page_size: 9：每页显示 9 个候选词。 ascii_composer/switch_key/Caps_Lock: commit_code：输入拼音过程中按下 Caps Lock 时，将当前尚未上屏的拼音字母原样提交为英文，而不是清空输入内容。 例如：\n1 2 3 输入：hello 按下 Caps Lock 结果：hello 直接上屏 重新部署 让鼠须管重新加载雾凇拼音和主题配置：\n1 \u0026#34;/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel\u0026#34; --reload 安装完成后，系统中主要保留：\n1 2 /Library/Input Methods/Squirrel.app ~/Library/Rime/ 如果鼠须管仍未出现在输入法列表中，可以注销当前 macOS 用户后重新登录。\n延伸阅读 Linux 使用 IBus Rime 安装雾凇拼音 ","date":"2026-09-15T00:00:00Z","permalink":"/posts/macos-rime-ice/","title":"macOS 安装鼠须管与雾凇拼音"},{"content":"适用范围 NVIDIA 的 Linux .run 安装器并非 Ubuntu 专属。下载驱动、确认 GPU 与内核兼容性、退出图形会话并运行安装器属于通用流程；开发包名称、initramfs 更新、显示服务与 Secure Boot 配置需要按发行版处理。\n下面以 Debian/Ubuntu 的依赖与 initramfs 命令为例。其他发行版使用对应工具；驱动类型和支持的 GPU 以所选安装包文档为准。\n下载驱动 NVIDIA 驱动程序下载\n禁用 Nouveau：通用配置与发行版步骤 1 2 printf \u0026#39;%s\\n\u0026#39; \u0026#39;blacklist nouveau\u0026#39; \u0026#39;options nouveau modeset=0\u0026#39; \\ | sudo tee /etc/modprobe.d/blacklist-nouveau.conf 对使用 modprobe 配置的系统，上面写入规则的方式可复用。initramfs 如何更新由发行版工具决定；Debian/Ubuntu 示例：\n1 sudo update-initramfs -u Fedora/RHEL 等通常使用不同工具，不能只把 apt 换成 dnf 就套用整篇命令。按所选驱动与发行版文档重启并确认 Nouveau 不再占用设备。\n安装依赖（Debian/Ubuntu 示例） 1 2 sudo apt-get update \\ \u0026amp;\u0026amp; sudo apt-get install -y make gcc \u0026#34;linux-headers-$(uname -r)\u0026#34; 运行 NVIDIA 安装器 先按目标系统方式退出图形会话或停止占用 GPU 的显示服务，确认内核头文件与正在运行的内核匹配。需要模块签名的系统还应完成 Secure Boot 所需配置，再运行实际下载的文件：\n1 sudo ./NVIDIA-Linux-archname-xxx.xxx.xxx.run 参考：NVIDIA Linux 安装器文档。链接对应特定驱动分支，实际安装应查阅所选版本随附的 README。\n","date":"2024-11-29T00:00:00Z","permalink":"/posts/linux-nvidia-driver/","title":"Linux 使用 NVIDIA .run 安装驱动（Ubuntu 示例）"},{"content":" 以下安装命令使用 Ubuntu 24.04（noble）软件源，适用于该版本的 Ubuntu。风扇和背光功能取决于具体 Clevo 硬件与驱动，其他发行版需使用对应软件源。\n前言 由于蓝天主板大部分都是用Windows玩游戏，玩Linux的用户少之又少，官方也不重视这个市场，经过我研究发现一款工具可以实现部分风扇控制和背光控制的功能\nUbuntu 24.04 安装记录 1 2 3 echo \u0026#39;deb https://deb.tuxedocomputers.com/ubuntu noble main\u0026#39; | sudo tee /etc/apt/sources.list.d/tuxedo-computers.list \\ \u0026amp;\u0026amp; wget https://deb.tuxedocomputers.com/ubuntu/pool/main/t/tuxedo-archive-keyring/tuxedo-archive-keyring_2022.04.01~tux_all.deb \u0026amp;\u0026amp; sudo dpkg -i tuxedo-archive-keyring_2022.04.01~tux_all.deb \u0026amp;\u0026amp; rm -rf tuxedo-archive-keyring_2022.04.01~tux_all.deb \\ \u0026amp;\u0026amp; sudo apt update \u0026amp;\u0026amp; sudo apt-get install -y tuxedo-control-center ","date":"2024-11-28T00:00:00Z","permalink":"/posts/linux-tuxedo-control-center/","title":"Linux 为蓝天笔记本安装 TUXEDO Control Center"},{"content":"适用范围 雾凇拼音是 Rime 的输入方案与词库，配置本身不依赖 Ubuntu。本文以 Linux 的 IBus Rime 为前端，包安装以 Debian/Ubuntu 为例。其他发行版使用各自的包管理器安装 IBus Rime；Fcitx/Fcitx5 用户还需使用对应前端与用户配置目录。\n安装 IBus Rime Debian/Ubuntu 示例：\n1 2 sudo apt-get update sudo apt-get install -y ibus-rime git curl 登录图形桌面后，确认系统使用 IBus，在输入法设置中添加 Rime。具体入口与会话启动方式取决于桌面环境，不是所有发行版都使用同一设置界面。\n获取 Plum 配置管理器 使用 Plum 官方的一行安装命令，安装器会自动获取配置管理器及预设配方：\n1 curl -fsSL https://raw.githubusercontent.com/rime/plum/master/rime-install | bash 安装雾凇拼音 先备份已有 Rime 用户配置，再在用户权限下执行：\n1 2 cd plum \\ \u0026amp;\u0026amp; bash rime-install iDvel/rime-ice:others/recipes/full others/recipes/full 是完整安装配方。Plum 默认支持 IBus Rime；使用其他前端时，按实际目录指定 rime_dir，例如：\n1 2 # Fcitx5 示例：先确认此目录就是当前输入法使用的 Rime 用户目录。 rime_dir=\u0026#34;$HOME/.local/share/fcitx5/rime\u0026#34; bash rime-install iDvel/rime-ice:others/recipes/full 不要将 IBus 与 Fcitx5 的配置目录混用。\n重新部署 在对应输入法菜单中重新部署，选择雾凇拼音方案，测试中文输入。安装配方和桌面会话设置按实际发行版与前端调整。\n参考：雾凇拼音安装说明、Plum 配置管理器、IBus Rime。\n延伸阅读 macOS 安装鼠须管与雾凇拼音 ","date":"2024-11-28T00:00:00Z","permalink":"/posts/linux-rime-ice/","title":"Linux 使用 IBus Rime 安装雾凇拼音"},{"content":"适用于 Apple Silicon Mac。\n粘贴至Terminal即可自动安装 1 softwareupdate --install-rosetta --agree-to-license ","date":"2024-06-02T00:00:00Z","permalink":"/posts/macos-rosetta/","title":"Apple Silicon Mac 安装 Rosetta 2"},{"content":"一键安装命令 1 docker run -d --restart unless-stopped --name hoppscotch -p 8888:3000 hoppscotch/hoppscotch-frontend:latest http://IP:8888/ 为前端访问地址。这个命令只运行前端，并不包含自托管后端、数据库和后台管理服务。完整部署请参考 Hoppscotch 自托管文档。\n验证与停止 1 2 docker inspect -f \u0026#39;{{.State.Status}}\u0026#39; hoppscotch docker logs --tail=50 hoppscotch 访问前端并测试一个可用 API 请求；跨域、认证或后端连接仍需按实际部署配置。需要临时停止前端服务时执行：\n1 docker stop hoppscotch 延伸阅读 Linux 安装 Docker Engine 与 Docker Compose PHP 调用 gRPC：依赖、代码生成与客户端示例 ","date":"2023-10-25T00:00:00Z","permalink":"/posts/docker-hoppscotch/","title":"Docker 部署 Hoppscotch 前端"},{"content":"以下命令自动下载最新发布版本，并设置 SUID，供普通用户直接运行 NextTrace。安装后可用 nexttrace --version 检查版本；Linux 也可按 官方权限说明 使用 capabilities 授权。\nMac一键安装NextTrace 1 2 3 4 5 sudo mkdir -p /usr/local/bin \\ \u0026amp;\u0026amp; sudo curl -fsSL -o /usr/local/bin/nexttrace \u0026#34;https://github.com/nxtrace/NTrace-core/releases/latest/download/nexttrace_$(uname -s | tr \u0026#39;[:upper:]\u0026#39; \u0026#39;[:lower:]\u0026#39;)_$(uname -m | sed \u0026#39;s/x86_64/amd64/;s/aarch64/arm64/\u0026#39;)\u0026#34; \\ \u0026amp;\u0026amp; sudo xattr -rc /usr/local/bin/nexttrace \\ \u0026amp;\u0026amp; sudo chown root:admin /usr/local/bin/nexttrace \\ \u0026amp;\u0026amp; sudo chmod +sx /usr/local/bin/nexttrace Linux一键安装NextTrace 1 2 3 4 sudo mkdir -p /usr/local/bin \\ \u0026amp;\u0026amp; sudo curl -fsSL -o /usr/local/bin/nexttrace \u0026#34;https://github.com/nxtrace/NTrace-core/releases/latest/download/nexttrace_$(uname -s | tr \u0026#39;[:upper:]\u0026#39; \u0026#39;[:lower:]\u0026#39;)_$(uname -m | sed \u0026#39;s/x86_64/amd64/;s/aarch64/arm64/\u0026#39;)\u0026#34; \\ \u0026amp;\u0026amp; sudo chown root:root /usr/local/bin/nexttrace \\ \u0026amp;\u0026amp; sudo chmod +sx /usr/local/bin/nexttrace 延伸阅读 macOS 与 Linux 安装 iNetSpeed CLI macOS 安装 Ookla Speedtest CLI ","date":"2023-09-30T00:00:00Z","permalink":"/posts/nexttrace-install/","title":"macOS 与 Linux 安装 NextTrace"},{"content":"前言 由于腾讯云官方的SDK需要引用一大堆库和文件，使用起来非常不方便，因此我根据腾讯云的签名方法，自己写了一个腾讯云的精简版PHP-SDK，这个SDK只需要一个php文件即可实现，里面包含了签名方法、请求方法，以及用于调用的方法。\n这个SDK是以腾讯云短信服务为例，其他腾讯云服务只需要改一下里面的参数也可以用。\nSDK代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 \u0026lt;?php //腾讯云短信服务SDK class QcloudSimple { private $SecretId; private $SecretKey; private $endpoint = \u0026#34;sms.tencentcloudapi.com\u0026#34;; //接口请求域名 private $service = \u0026#34;sms\u0026#34;; //服务名称 private $version = \u0026#34;2021-01-11\u0026#34;; //API版本号 private $region = \u0026#34;ap-guangzhou\u0026#34;; //地域参数 /** * 初始化 * @access public * @param string $SecretId 腾讯云SecretId * @param string $SecretKey 腾讯云SecretKey */ public function __construct($SecretId,$SecretKey) { $this-\u0026gt;SecretId = $SecretId; $this-\u0026gt;SecretKey = $SecretKey; } /** * 短信发送 * @access public * @param string|array $PhoneNumber 手机号 * @param string $AppId 应用ID * @param string $TemplateId 模板ID * @param string|array|null $TemplateParam 模板参数 * @param string|null $SignName 短信签名 * @param string|null $ExtendCode 短信码号扩展号 * @param string|null $SessionContext 用户的 session 内容 * @param string|null $SenderId 国内短信无需填写该项 * @return false|mixed */ public function SendSms($PhoneNumber, $AppId, $TemplateId, $TemplateParam = null, $SignName = null, $ExtendCode = null, $SessionContext = null, $SenderId = null) { $action = \u0026#39;SendSms\u0026#39;; $phones = is_array($PhoneNumber) ? $PhoneNumber : [$PhoneNumber]; $param = [ \u0026#39;PhoneNumberSet\u0026#39; =\u0026gt; array_values(array_map(\u0026#39;strval\u0026#39;, $phones)), \u0026#39;SmsSdkAppId\u0026#39; =\u0026gt; (string) $AppId, \u0026#39;TemplateId\u0026#39; =\u0026gt; (string) $TemplateId, ]; if ($TemplateParam !== null \u0026amp;\u0026amp; $TemplateParam !== \u0026#39;\u0026#39;) { $values = is_array($TemplateParam) ? $TemplateParam : [$TemplateParam]; $param[\u0026#39;TemplateParamSet\u0026#39;] = array_values(array_map(\u0026#39;strval\u0026#39;, $values)); } $optional = [ \u0026#39;SignName\u0026#39; =\u0026gt; $SignName, \u0026#39;ExtendCode\u0026#39; =\u0026gt; $ExtendCode, \u0026#39;SessionContext\u0026#39; =\u0026gt; $SessionContext, \u0026#39;SenderId\u0026#39; =\u0026gt; $SenderId, ]; foreach ($optional as $name =\u0026gt; $value) { if ($value !== null \u0026amp;\u0026amp; $value !== \u0026#39;\u0026#39;) { $param[$name] = (string) $value; } } return $this-\u0026gt;send_request($action, $param); } private function send_request($action, $param) { $payload = json_encode($param, JSON_UNESCAPED_UNICODE); if ($payload === false) { return false; } $time = time(); $authorization = $this-\u0026gt;generateSign($payload, $time); $header = [\u0026#39;Authorization: \u0026#39; . $authorization, \u0026#39;Content-Type: application/json; charset=utf-8\u0026#39;, \u0026#39;X-TC-Action: \u0026#39; . $action, \u0026#39;X-TC-Timestamp: \u0026#39; . $time, \u0026#39;X-TC-Version: \u0026#39; . $this-\u0026gt;version, \u0026#39;X-TC-Region: \u0026#39; . $this-\u0026gt;region]; return $this-\u0026gt;curl_post($payload, $header); } private function generateSign($payload, $time) { $algorithm = \u0026#34;TC3-HMAC-SHA256\u0026#34;; // step 1: build canonical request string $httpRequestMethod = \u0026#34;POST\u0026#34;; $canonicalUri = \u0026#34;/\u0026#34;; $canonicalQueryString = \u0026#34;\u0026#34;; $canonicalHeaders = \u0026#34;content-type:application/json; charset=utf-8\\n\u0026#34; . \u0026#34;host:\u0026#34; . $this-\u0026gt;endpoint . \u0026#34;\\n\u0026#34;; $signedHeaders = \u0026#34;content-type;host\u0026#34;; $hashedRequestPayload = hash(\u0026#34;SHA256\u0026#34;, $payload); $canonicalRequest = $httpRequestMethod . \u0026#34;\\n\u0026#34; . $canonicalUri . \u0026#34;\\n\u0026#34; . $canonicalQueryString . \u0026#34;\\n\u0026#34; . $canonicalHeaders . \u0026#34;\\n\u0026#34; . $signedHeaders . \u0026#34;\\n\u0026#34; . $hashedRequestPayload; // step 2: build string to sign $date = gmdate(\u0026#34;Y-m-d\u0026#34;, $time); $credentialScope = $date . \u0026#34;/\u0026#34; . $this-\u0026gt;service . \u0026#34;/tc3_request\u0026#34;; $hashedCanonicalRequest = hash(\u0026#34;SHA256\u0026#34;, $canonicalRequest); $stringToSign = $algorithm . \u0026#34;\\n\u0026#34; . $time . \u0026#34;\\n\u0026#34; . $credentialScope . \u0026#34;\\n\u0026#34; . $hashedCanonicalRequest; // step 3: sign string $secretDate = hash_hmac(\u0026#34;SHA256\u0026#34;, $date, \u0026#34;TC3\u0026#34; . $this-\u0026gt;SecretKey, true); $secretService = hash_hmac(\u0026#34;SHA256\u0026#34;, $this-\u0026gt;service, $secretDate, true); $secretSigning = hash_hmac(\u0026#34;SHA256\u0026#34;, \u0026#34;tc3_request\u0026#34;, $secretService, true); $signature = hash_hmac(\u0026#34;SHA256\u0026#34;, $stringToSign, $secretSigning); // step 4: build authorization $authorization = $algorithm . \u0026#34; Credential=\u0026#34; . $this-\u0026gt;SecretId . \u0026#34;/\u0026#34; . $credentialScope . \u0026#34;, SignedHeaders=content-type;host, Signature=\u0026#34; . $signature; return $authorization; } private function curl_post($payload, $header) { $url = \u0026#39;https://\u0026#39; . $this-\u0026gt;endpoint . \u0026#39;/\u0026#39;; $ch = curl_init($url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); $json = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode == 200) { $arr = json_decode($json, true); return isset($arr[\u0026#39;Response\u0026#39;]) ? $arr[\u0026#39;Response\u0026#39;] : false; } else { return false; } } } 调用前检查 需要 PHP 的 cURL 扩展以及可用的系统 CA 证书。保留 TLS 校验；证书错误时修复 CA 配置，不要关闭校验。手机号使用 API 要求的国际区号格式，例如 +8613812345678。\nfalse 表示传输或响应解析失败。返回数组时仍需检查 Error 字段，HTTP 200 不等于短信已发送成功。签名名称、模板和应用 ID 必须使用已审核且互相匹配的实际配置。\n参考：腾讯云 SendSms API。\n延伸阅读 Composer 指定依赖解析的 PHP 版本 ","date":"2023-06-08T00:00:00Z","permalink":"/posts/php-tencent-sms/","title":"PHP 实现腾讯云短信 API 的简化客户端"},{"content":"前言 最近写项目的时候碰到一些api接口需要使用gRPC调用，本着php是世界上最好的语言的前提，简单研究了一下使用方法。\n扩展安装 1 pecl install grpc 直接使用pecl命令编译，简单省事，如需手动编译建议自行谷歌。\n1 extension=grpc.so 将此行添加到 php.ini 文件中的任意位置，例如 /etc/php/php.ini。\nprotoc编译器安装 1 2 sudo apt-get update sudo apt-get install -y cmake git make g++ 上述依赖命令适用于 Debian/Ubuntu。其他发行版的包名可能不同，不能仅替换包管理器名称。\n1 2 3 4 5 6 7 git clone --recurse-submodules https://github.com/grpc/grpc cd grpc git submodule update --init --recursive mkdir -p cmake/build cd cmake/build cmake ../.. -DgRPC_BUILD_TESTS=OFF make protoc grpc_php_plugin protoc：Protocol Buffers 编译器，用于生成消息类；服务客户端由下面的插件生成。\ngrpc_php_plugin：protoc的插件，用于生成服务存根类。\n1 2 sudo install -m 0755 third_party/protobuf/protoc /usr/local/bin/protoc sudo install -m 0755 grpc_php_plugin /usr/local/bin/grpc_php_plugin 将二进制文件移动到/usr/local/bin目录。\n项目依赖与代码生成 以下步骤在自己的 PHP 项目根目录执行，不是在 gRPC 的编译目录中执行：\n1 2 composer require grpc/grpc google/protobuf mkdir -p proto src 将自己的 .proto 文件放到 proto 目录及其子目录。使用本文示例时，先创建 proto/helloworld.proto，内容如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 syntax = \u0026#34;proto3\u0026#34;; package Services.Base.HelloWorld; service HelloWorldService { rpc Demo (HelloWorldRequest) returns (HelloWorldResponse); } message HelloWorldRequest { string name = 1; } message HelloWorldResponse { string message = 1; } 准备好协议文件后，进入协议目录，自动查找全部 .proto，批量生成消息类和服务客户端到上一级 src：\n1 2 3 4 cd proto \\ \u0026amp;\u0026amp; mkdir -p ../src \\ \u0026amp;\u0026amp; protoc --proto_path=./ --php_out=../src --grpc_out=../src \\ --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin $(find . -type f -name \u0026#34;*.proto\u0026#34;) 完成后回到 PHP 项目根目录：\n1 cd .. 本文示例的协议使用 Services.Base.HelloWorld 包名，生成类位于 src/Services；将以下内容合并到现有 composer.json 的自动加载配置中。使用自己的协议时，命名空间与目录按生成结果调整：\n1 2 3 4 5 6 7 8 9 { \u0026#34;autoload\u0026#34;: { \u0026#34;psr-4\u0026#34;: { \u0026#34;App\\\\\u0026#34;: \u0026#34;src/App/\u0026#34;, \u0026#34;Services\\\\\u0026#34;: \u0026#34;src/Services/\u0026#34;, \u0026#34;GPBMetadata\\\\\u0026#34;: \u0026#34;src/GPBMetadata/\u0026#34; } } } 1 2 mkdir -p src/App composer dump-autoload 插件会生成服务客户端；下方手工 Client 用于解释调用路径，不需要替换生成文件。将其保存为 src/App/Client.php，调用类保存为 src/App/App.php。\n客户端代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 \u0026lt;?php namespace App; use Grpc\\BaseStub; use Services\\Base\\HelloWorld\\HelloWorldRequest; use Services\\Base\\HelloWorld\\HelloWorldResponse; class Client extends BaseStub { public function __construct($hostname, $opts, $channel = null) { parent::__construct($hostname, $opts, $channel); } /** * 用于请求和响应该服务 * @param HelloWorldRequest $argument * @param array $metadata * @param array $options * @return */ public function Demo(HelloWorldRequest $argument, $metadata = [], $options = []) { return $this-\u0026gt;_simpleRequest(\u0026#39;/Services.Base.HelloWorld.HelloWorldService/Demo\u0026#39;, $argument, [HelloWorldResponse::class, \u0026#39;decode\u0026#39;], $metadata, $options); } } 调用gRPC代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 \u0026lt;?php namespace App; use Grpc\\ChannelCredentials; use Services\\Base\\HelloWorld\\HelloWorldRequest; /** * grpc请求案例 */ class App { public $client; public static function run() { (new self())-\u0026gt;grpc()-\u0026gt;Main(); } public function grpc() { $this-\u0026gt;client = $this-\u0026gt;client(); return $this; } public function client() { return new Client(\u0026#39;127.0.0.1:8000\u0026#39;, [ \u0026#39;credentials\u0026#39; =\u0026gt; ChannelCredentials::createInsecure(), ]); } public function Main() { $request = new HelloWorldRequest(); $request-\u0026gt;setName(\u0026#34;林逸博客\u0026#34;); $get = $this-\u0026gt;client-\u0026gt;Demo($request)-\u0026gt;wait(); list($reply, $status) = $get; if ($status-\u0026gt;code === 0 \u0026amp;\u0026amp; $reply !== null) { print_r($reply-\u0026gt;getMessage()); } else { echo \u0026#39;获取返回内容失败：\u0026#39; . $status-\u0026gt;details; } } } 完整Demo https://github.com/zihelyu/php-grpc-client\n运行入口 在项目根目录创建 run.php：\n1 2 3 \u0026lt;?php require __DIR__ . \u0026#39;/vendor/autoload.php\u0026#39;; \\App\\App::run(); 运行前需要在 127.0.0.1:8000 启动实现相同协议的 gRPC 服务端。createInsecure() 仅适用于本文本地示例；远程服务按其要求配置 TLS。编译工具链和联调步骤参考 PHP gRPC 官方入门。\n服务端就绪后，在项目根目录执行 php run.php 发起调用。\n延伸阅读 Composer 指定依赖解析的 PHP 版本 Docker 部署 Hoppscotch 前端 ","date":"2023-06-01T00:00:00Z","permalink":"/posts/php-grpc/","title":"PHP 调用 gRPC：依赖、代码生成与客户端示例"},{"content":" 这是旧版 macOS 微信的操作记录。当前客户端可能移除或调整该入口；请先确认版本与选项，不保证所有版本可用。\n前言 从来没有见过如此厚颜无耻的人，可能是为了收集用户习惯吧，居然打开链接都内置浏览器了。\n解决办法 使用快捷键 Command + Option + Control + R 如果打开不了，可以在微信搜索框里输入“:recover” 进入该模式，然后打开「使用默认浏览器打开链接」选项\n","date":"2023-05-26T00:00:00Z","permalink":"/posts/macos-wechat-browser/","title":"macOS 微信使用默认浏览器打开链接"},{"content":" 历史记录：文中第三方解析服务、公众号与脚本为当时的使用记录，当前入口和服务状态可能变化。\n前置条件 Chrome浏览器或者Edge浏览器，至于什么百度、2345、360安全浏览器只要是Chromium套壳都可以，另外Firefox和Safari也是支持的。 Tampermonkey 扩展 一个充满智慧的大脑 魔法上网 (非必要) 百度云不限速油猴下载脚本 使用方法 先去装油猴插件 Chrome版本 Firefox版本 Safari版本 Edge版本 Opera版本 扩展商店地址或下载入口可能变化，打不开时请查阅扩展官方页面。 装完插件后点击前置条件第五步的链接安装百度云不限速下载脚本 去百度云下载界面你会看到一个下载设置的选项，在里面填上加速链接保存就可以了，加速链接关注微信公众号叮当的四次元口袋回复领取加速链接即可，这个公众号不是我的，这个东西原理没什么技术含量，我不做的原因是传播多了会吃百度的律师函。第三方服务的状态与可靠性需要自行核实。 我一般都是配合Aria2做多线程下载，不然浏览器下载的话是单线程的，而且浏览器下载也不稳定。 enjoy. 延伸阅读 Docker 部署 Aria2 RPC 下载服务 ","date":"2023-04-08T00:00:00Z","permalink":"/posts/baidu-netdisk-aria2/","title":"百度网盘下载脚本与 Aria2 使用记录（历史记录）"},{"content":"安装命令 使用 Docker 部署，重启策略为 unless-stopped：\n1 2 3 4 5 6 7 docker run -d \\ --name zigbee2mqtt \\ --restart unless-stopped \\ -p 8080:8080 \\ -e TZ=Asia/Shanghai \\ -v ~/zigbee2mqtt/data:/app/data \\ koenkk/zigbee2mqtt:latest 后台地址为 http://IP:8080/。配置文件位于 ~/zigbee2mqtt/data/configuration.yaml；MQTT 和网络协调器地址按自己的实际地址填写。\n2.x 及后续版本配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mqtt: base_topic: zigbee2mqtt server: mqtt://192.168.1.10:1883 user: admin password: public serial: port: tcp://192.168.1.20:8888 adapter: zstack advanced: channel: 15 frontend: enabled: true port: 8080 homeassistant: enabled: true 192.168.1.10:1883 是 MQTT 服务地址，192.168.1.20:8888 是网络协调器地址，均需替换为实际地址。账号和密码使用自己的 MQTT 凭据；adapter: zstack 对应 TI Z-Stack 协调器，其他类型使用对应的 adapter。\n通过命令开启配对 2.0 起移除了配置文件中的 permit_join 和永久允许加入功能。可通过 MQTT 命令开启配对，无需手工点页面。安装了 mosquitto_pub 的机器上执行：\n1 2 mosquitto_pub -h 192.168.1.10 -p 1883 -u admin -P public \\ -t zigbee2mqtt/bridge/request/permit_join -m \u0026#39;{\u0026#34;time\u0026#34;:254}\u0026#39; 将地址和凭据替换为配置中的实际值；新版单次配对窗口最多 254 秒，时间到了可再次执行。也可在前端开启配对。\n1.x 配置格式 使用 1.x 镜像的环境采用下面的格式。上面的 latest 部署使用新版格式，两组配置按镜像版本选择，不要合并：\n1 2 3 4 5 6 7 8 9 10 11 12 homeassistant: true permit_join: true mqtt: base_topic: zigbee2mqtt server: mqtt://192.168.1.10:1883 user: admin password: public serial: port: tcp://192.168.1.20:8888 advanced: channel: 15 frontend: true USB 协调器 使用 USB 时，将 serial.port 改成容器内设备路径，例如 /dev/ttyUSB0，并在创建容器时加入实际设备映射：\n1 2 --device=/dev/serial/by-id/实际设备ID:/dev/ttyUSB0 -v /run/udev:/run/udev:ro 参考：Docker 部署、2.0 配置迁移、MQTT 配对命令。\n延伸阅读 Linux 安装 Docker Engine 与 Docker Compose Docker 部署 Home Assistant Container ","date":"2023-03-29T00:00:00Z","permalink":"/posts/docker-zigbee2mqtt/","title":"Docker 部署 Zigbee2MQTT"},{"content":"安装命令 1 2 3 4 5 6 7 docker run -d \\ --name homeassistant \\ --restart unless-stopped \\ -e TZ=Asia/Shanghai \\ -v ~/homeassistant/config:/config \\ --network=host \\ ghcr.io/home-assistant/home-assistant:stable 管理页面为 http://IP:8123/。此示例适用于 Linux 宿主机的 host 网络；Home Assistant Container 不包含 Supervisor 和附加组件商店。\n验证运行状态 1 2 docker inspect -f \u0026#39;{{.State.Status}}\u0026#39; homeassistant docker logs --tail=50 homeassistant 容器状态应为 running，然后访问管理页面完成初始化。若反复重启，先检查日志和配置目录权限。\n备份与恢复 升级前备份 ~/homeassistant/config，并记录原镜像版本。升级回退需要按 Home Assistant 对应版本的要求恢复备份及镜像，避免仅切换标签后复用不兼容的数据。\n临时停止容器 停止服务时执行，配置目录会保留：\n1 docker stop homeassistant 延伸阅读 Linux 安装 Docker Engine 与 Docker Compose Docker 部署 Zigbee2MQTT ","date":"2023-03-29T00:00:00Z","permalink":"/posts/docker-home-assistant/","title":"Docker 部署 Home Assistant Container"},{"content":"适用范围 本文创建的是 Swap 文件，不是独立磁盘分区。以下示例适用于支持普通 Swap 文件的 ext4 等文件系统；Btrfs、写时复制文件系统需要使用其专用流程。命令以管理员权限执行。\n先检查现有交换空间，确认 /swapfile 尚不存在：\n1 2 3 swapon --show free -h ls -l /swapfile 若文件已存在，先检查它是否正在使用，不要覆盖。\n创建 1 GiB Swap 文件 1 2 3 4 sudo sh -c \u0026#39;test ! -e /swapfile \u0026amp;\u0026amp; umask 077 \u0026amp;\u0026amp; dd if=/dev/zero of=/swapfile bs=1M count=1024\u0026#39; sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile 确认创建命令成功后再执行后续步骤。bs × count 是文件大小，这里是 1024 MiB。\n开机自动启用 检查 /etc/fstab 中是否已有该文件的配置，避免重复添加：\n1 grep -F \u0026#39;/swapfile \u0026#39; /etc/fstab 如果没有，再添加：\n1 printf \u0026#39;/swapfile none swap sw 0 0\\n\u0026#39; | sudo tee -a /etc/fstab 验证 1 2 swapon --show free -h 确认输出包含 /swapfile，并在重启后再次检查。\n延伸阅读 Linux 数据盘分区、格式化与挂载 ","date":"2023-03-27T00:00:00Z","permalink":"/posts/linux-swap/","title":"Linux 创建并启用 Swap 文件"},{"content":"以下命令要求系统已安装 zsh、git 和 curl，并以需要使用该配置的用户身份执行。\n运行下面的命令一键安装ohmyzsh 1 sh -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; 一键安装命令自动建议插件 1 2 git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions.git \u0026#34;${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/zsh-autosuggestions\u0026#34; \\ \u0026amp;\u0026amp; sed -i.bak \u0026#39;s/^plugins=(\\(.*\\))/plugins=(\\1 zsh-autosuggestions)/\u0026#39; ~/.zshrc ","date":"2023-03-27T00:00:00Z","permalink":"/posts/linux-oh-my-zsh/","title":"Linux 安装 Oh My Zsh 与 zsh-autosuggestions"},{"content":" 仅适用于兼容的 Intel Mac。Apple Silicon 不使用该框架；本历史安装记录中的第三方下载资产与系统兼容性需要重新确认。\n下载IntelPowerGadget.framework 1 curl -fsSL https://github.com/zihelyu/IntelPowerGadget/releases/latest/download/IntelPowerGadget.framework.zip -o /tmp/IntelPowerGadget.framework.zip 解压安装 1 sudo unzip -oq /tmp/IntelPowerGadget.framework.zip -d /Library/Frameworks \u0026amp;\u0026amp; sudo xattr -rc /Library/Frameworks/IntelPowerGadget.framework ","date":"2023-03-27T00:00:00Z","permalink":"/posts/macos-intel-power-gadget/","title":"Intel Mac 安装 IntelPowerGadget.framework"},{"content":"适用范围 firewalld 可用于多种 Linux 发行版，不属于某个发行版独有功能。以下命令要求系统已经安装 firewalld，且由 systemd 管理；使用其他防火墙或初始化系统时不能直接套用。\n查看当前状态（可选） 需要恢复原状态时，可在修改前记录服务是否运行、是否开机启动：\n1 2 systemctl is-active firewalld systemctl is-enabled firewalld 临时关闭 停止当前运行的服务：\n1 systemctl stop firewalld 永久禁用 取消开机启动。disable 不会停止当前运行的服务；需要立即关闭并取消开机启动时，依次执行上面的 stop 和下面的 disable：\n1 systemctl disable firewalld.service 验证与恢复 修改后用上面的查询命令检查：执行 stop 后应为 inactive，执行 disable 后应为 disabled；这些状态可能返回非零退出码。若原来是运行且开机启用的状态，需要恢复时执行：\n1 sudo systemctl enable --now firewalld 原本未开机启用的系统，应按原状态单独启动服务或保持禁用。\n参考：systemctl 官方说明。\n","date":"2023-02-21T00:00:00Z","permalink":"/posts/linux-disable-firewalld/","title":"Linux 临时关闭与永久禁用 firewalld"},{"content":"前言 由于默认的Sublime Text配置不是很好用，经过我使用一段时间后整理了一些不错的配置。\n配置代码 1 2 3 4 5 6 7 8 9 10 { \u0026#34;hot_exit\u0026#34;: false, // 关闭热退出（不在关闭时保存未保存的会话） \u0026#34;remember_open_files\u0026#34;: false, // 关闭记住上次打开的文件 \u0026#34;enable_hexadecimal_encoding\u0026#34;: false, //不启用16进制编辑器 \u0026#34;font_size\u0026#34;:14, //设置字体大小 \u0026#34;word_wrap\u0026#34;:true, //自动换行 \u0026#34;save_on_focus_lost\u0026#34;:true, //失去焦点自动保存 \u0026#34;preview_on_click\u0026#34;:false, //取消单击预览 \u0026#34;update_check\u0026#34;:false //取消自动更新 } ","date":"2022-12-10T00:00:00Z","permalink":"/posts/sublime-text-settings/","title":"Sublime Text 常用配置"},{"content":"适用范围 需要以文件所有者或具备相应权限的管理员身份执行；仅有目录写入权限并不足以修改其他用户文件的权限。下面给出两种把目录统一设为 755、文件统一设为 644 的方法，以及保留可执行文件的方法，路径替换为实际目录。\n方法一：一次递归 chmod 1 chmod -R a=r,u+w,a+X \u0026#34;/path/to/directory\u0026#34; a=r 先把权限设为只读并清除已有执行位，u+w 为所有者增加写权限，最后 a+X 为目录恢复执行位。最终目录是 755，普通文件和原可执行文件都会变成 644。\n方法二：按文件类型精确设置 1 2 find \u0026#34;/path/to/directory\u0026#34; -type f -exec chmod 644 {} + \\ -o -type d -exec chmod 755 {} + 这个方法明确区分普通文件与目录，符号链接不匹配 -type f 或 -type d。不要先递归把目录全部设为 644，再尝试进入目录恢复权限；普通用户可能因此失去遍历权限。\n可选：保留已有可执行文件 如果目录中包含需要执行的脚本或程序，可以使用：\n1 chmod -R u=rwX,go=rX \u0026#34;/path/to/directory\u0026#34; 目录变成 755，原先有执行位的文件保留执行能力并变成 755，其余文件变成 644。此方法与前两种严格的“所有文件 644”目标不同，应按目录用途选择。\n验证结果 在目标目录抽查目录、普通文件与可执行文件，例如：\n1 stat -c \u0026#39;%a %n\u0026#39; \u0026#34;/path/to/directory\u0026#34; \u0026#34;/path/to/directory/example.txt\u0026#34; 前两种方法将所有普通文件设为 644，第三种会保留原有可执行文件的执行权限，抽查时按所选方法确认结果。\n参考：GNU C Library 的文件权限说明。\n延伸阅读 Linux 数据盘分区、格式化与挂载 ","date":"2022-08-19T00:00:00Z","permalink":"/posts/linux-chmod-755-644/","title":"Linux 将目录权限设为 755、文件权限设为 644"},{"content":"前言 众所周知惠普的1020打印机是打印成本很低的一款打印机，缺点就是只支持USB链接，无法通过网络链接，更别提AirPrint了，所以我使用Docker安装CUPS服务完美解决了这一大难题。\n下载镜像配置 1 git clone --depth=1 https://github.com/zihelyu/docker_cups 切换到构建目录 1 cd docker_cups/services 构建镜像 1 docker-compose up -d 访问WEB控制页面添加打印机 后台地址为 http://ip:631/admin\n管理员账号为root，密码为password\n点击Add Printer添加打印机并勾选Share This Printer（共享此打印机）\n配置热插拔 因为使用Docker运行，打印机USB断开会导致打印机固件失效，所以需要配置热插拔检测到打印机重新插入重启容器。\n1 ACTION==\u0026#34;add\u0026#34;, SUBSYSTEM==\u0026#34;usb\u0026#34;, ENV{DEVTYPE}==\u0026#34;usb_device\u0026#34;, ENV{PRODUCT}==\u0026#34;3f0/2b17/100\u0026#34;, RUN+=\u0026#34;/usr/bin/sh -c \u0026#39;/usr/bin/sleep 10 \u0026amp;\u0026amp; /usr/bin/docker restart cups\u0026#39;\u0026#34; 将上面的命令保存到/etc/udev/rules.d/Hp-1020-print.rules文件\n延伸阅读 Linux 安装 Docker Engine 与 Docker Compose ","date":"2022-08-16T00:00:00Z","permalink":"/posts/docker-cups-hp1020/","title":"Docker 部署 CUPS：共享 HP LaserJet 1020 打印机"},{"content":" 这是旧款小米路由器的历史算法记录，不能保证适用于全部机型和固件。计算出密码不等于设备已经启用 SSH/root 登录。\n前言 由于小米官方获取root密码的步骤过于繁琐，故编写php脚本进行计算，代码如下。\n计算代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #!/usr/bin/env php \u0026lt;?php //路由器固件解包后，从 /bin/mkxqimage 中提取的盐 $salt = array( \u0026#39;r1d\u0026#39; =\u0026gt; \u0026#39;A2E371B0-B34B-48A5-8C40-A7133F3B5D88\u0026#39;, \u0026#39;others\u0026#39; =\u0026gt; \u0026#39;d44fb0960aa0-a5e6-4a30-250f-6d2df50a\u0026#39; ); isset($argv[1]) or die(\u0026#39;Usage: \u0026#39; . $argv[0] . \u0026#34; SN\\n\u0026#34;); print_line(get_passwd($argv[1])); function print_line($message) { echo $message . \u0026#34;\\n\u0026#34;; } //密码算法： //原始 SN 拼接反转后的盐，做 md5 运算取前 8 个字符 function get_passwd($sn) { return substr(md5($sn . get_salt($sn)), 0, 8); } // SN 中不含 \u0026#39;/\u0026#39; 则为 r1d function get_salt($sn) { global $salt; if (false === strpos($sn, \u0026#39;/\u0026#39;)) { return $salt[\u0026#39;r1d\u0026#39;]; } else { return swap_salt($salt[\u0026#39;others\u0026#39;]); } } //非 R1D 盐要反转后才能使用 function swap_salt($salt) { return implode(\u0026#39;-\u0026#39;, array_reverse(explode(\u0026#39;-\u0026#39;, $salt))); } 使用教程 1 php password.php \u0026#34;实际路由器SN\u0026#34; password.php 为计算代码保存的文件名。\n延伸阅读 Docker 编译 ImmortalWrt（Ubuntu 24.04 容器示例） ","date":"2022-08-15T00:00:00Z","permalink":"/posts/xiaomi-router-root-password/","title":"PHP 根据小米路由器 SN 计算历史 root 密码"},{"content":"前言 执行 yarn add sass-loader 时，依赖要求 Node.js 至少为 14.15.0，而当前环境是 12.22.12，因此 Yarn 拒绝安装：\n1 error sass-loader@13.0.2: The engine \u0026#34;node\u0026#34; is incompatible with this module. Expected version \u0026#34;\u0026gt;= 14.15.0\u0026#34;. Got \u0026#34;12.22.12\u0026#34; 设置 Yarn Classic 跳过版本检查 1 yarn config set ignore-engines true 该设置会用于后续 Yarn Classic 安装操作，执行后重新运行 yarn add sass-loader。它跳过的是依赖的 engines 检查，不会升级 Node.js；安装后的依赖能否运行，仍取决于它使用的 Node.js 功能。\n参考：Yarn Classic 配置、ignore-engines 选项。\n","date":"2022-08-14T00:00:00Z","permalink":"/posts/yarn-ignore-engines/","title":"Yarn Classic 处理 Node.js engines 版本不兼容"},{"content":"前言 下面的命令自动下载 Ookla Speedtest CLI 1.2.0 的 macOS 压缩包，解压后安装到 /usr/local/bin/speedtest。\niNetSpeed CLI 也使用该安装路径，两者先后安装时，后安装的会覆盖已有程序。\n安装命令 1 2 3 4 5 6 7 cd /tmp \\ \u0026amp;\u0026amp; curl -fsSOL https://install.speedtest.net/app/cli/ookla-speedtest-1.2.0-macosx-universal.tgz \\ \u0026amp;\u0026amp; tar -zxf ookla-speedtest-1.2.0-macosx-universal.tgz \\ \u0026amp;\u0026amp; sudo xattr -rc speedtest \\ \u0026amp;\u0026amp; sudo mkdir -p /usr/local/bin \\ \u0026amp;\u0026amp; sudo mv speedtest /usr/local/bin \\ \u0026amp;\u0026amp; cd 验证安装 1 speedtest -V 如果有输出版本号就说明安装成功了\n延伸阅读 macOS 与 Linux 安装 iNetSpeed CLI macOS 与 Linux 安装 NextTrace ","date":"2022-08-14T00:00:00Z","permalink":"/posts/macos-speedtest/","title":"macOS 安装 Ookla Speedtest CLI"},{"content":" 历史记录：Apple 已于 2026-02-10 结束旧版家庭架构支持，详见 官方说明。下方记录旧版系统使用描述文件的方法。\n前言 由于苹果公司从IOS16版本开始对家庭架构进行了升级，并且升级后居然不能向下兼容，由于我手中还有低版本的旧设备所以我宁可不使用新特性也要兼容旧设备。\n安装旧版架构描述文件 http://appldnld.apple.com/iOSProfiles/KeepLegacyHome.mobileconfig\n这个描述文件的作用是保留使用旧版架构，安装完需要重新创建一个新的家庭才可以，会删除掉之前配置的所有配件以及自动化，降级的话不可以保留。\n安装重置HomeKit描述文件 http://appldnld.apple.com/iOSProfiles/HomeKitReset.mobileconfig\n这个描述文件是重置HomeKit的，非必装，作用就是自动重建HomeKit不需要手动去删除了。\n","date":"2022-04-05T00:00:00Z","permalink":"/posts/ios16-home-architecture/","title":"iOS 16 保留旧版家庭架构的方法（历史记录）"},{"content":"指定目标 PHP 版本 当开发环境与部署环境的 PHP 版本不同时，可以让 Composer 按部署环境解析依赖。下面以历史项目的 PHP 7.2.34 为例，版本值应替换为实际目标版本。\n在现有 composer.json 的 config 中合并以下设置：\n1 2 3 4 5 6 7 { \u0026#34;config\u0026#34;: { \u0026#34;platform\u0026#34;: { \u0026#34;php\u0026#34;: \u0026#34;7.2.34\u0026#34; } } } config.platform.php 只模拟 Composer 解析依赖时看到的 PHP 版本，不会安装 PHP、切换解释器，也不能保证应用代码兼容旧版本。\n重新解析依赖 保存配置后执行：\n1 composer update Composer 会按目标 PHP 版本重新解析并更新 composer.lock，无需先删除锁文件。\n参考：Composer platform 配置。\n延伸阅读 PHP 调用 gRPC：依赖、代码生成与客户端示例 ","date":"2021-12-30T00:00:00Z","permalink":"/posts/composer-php-version/","title":"Composer 指定依赖解析的 PHP 版本"},{"content":" 旧安装包的 Payload 格式与 CPU 架构可能变化，请按所用版本核对手工解包流程。使用前核对下载包内容，并准备或导入属于自己的签名密钥。\n获取GnuPG LTS版本 1 gnupg_version=`curl -fsSL https://sourceforge.net/p/gpgosx/docu/Download/ | grep -oE \u0026#39;The latest LTS version of GnuPG for OS X is [^()[:space:]]+\u0026#39; | sed -E \u0026#39;s/The latest LTS version of GnuPG for OS X is //\u0026#39;` 下载GnuPG LTS 1 curl -fsSL \u0026#34;https://jaist.dl.sourceforge.net/project/gpgosx/GnuPG-${gnupg_version}.dmg\u0026#34; -o /tmp/GnuPG-${gnupg_version}.dmg 挂载dmg文件 1 hdiutil attach \u0026#34;/tmp/GnuPG-${gnupg_version}.dmg\u0026#34; -noverify -mountpoint /Volumes/GnuPG 解压安装文件到临时目录 1 mkdir -p /tmp/GnuPG/gnupg-2.2 \u0026amp;\u0026amp; xar -xf /Volumes/GnuPG/Install.pkg -C /tmp/GnuPG \u0026amp;\u0026amp; cd /tmp/GnuPG/gnupg-2.2 \u0026amp;\u0026amp; cat /tmp/GnuPG/GnuPG.pkg/Payload | cpio -i \u0026amp;\u0026amp; cd 安装GnuPG到/usr/local目录 1 sudo mv /tmp/GnuPG/gnupg-2.2 /usr/local/gnupg-2.2 \u0026amp;\u0026amp; sudo mkdir -p /usr/local/bin \u0026amp;\u0026amp; sudo ln -s /usr/local/gnupg-2.2/bin/gpg2 /usr/local/bin/gpg 查看已有签名密钥 1 gpg --list-secret-keys --keyid-format=long 配置Git签名 1 git config --global user.signingkey \u0026#34;YOUR_KEY_ID\u0026#34; 如果你只想当前仓库签名 1 git config commit.gpgsign true 如果所有仓库签名 1 git config --global commit.gpgsign true 关闭签名 下面的命令关闭当前仓库的自动签名。若需要关闭全局自动签名，使用 git config --global commit.gpgsign false：\n1 git config commit.gpgsign false 延伸阅读 Git 拉取 GitHub 未合并的 PR Linux 自动获取最新版 Git 并编译安装 ","date":"2021-12-10T00:00:00Z","permalink":"/posts/macos-git-gpg/","title":"macOS 安装 GnuPG 并配置 Git 提交签名"},{"content":"适用范围 本文自动获取 Git 最新正式版并编译安装。编译流程可用于多种 Linux 发行版，依赖安装按系统分别列出 Debian/Ubuntu、Fedora 和旧 CentOS 7 示例。\n下面只构建 Git 程序，不包含额外文档生成。Git 版本与旧系统编译器、开发库的兼容性仍需确认；不能保证最新版可在旧 CentOS 7 上构建。\n按发行版安装依赖 Debian/Ubuntu 示例：\n1 2 3 sudo apt-get update sudo apt-get install -y build-essential autoconf dh-autoreconf \\ libcurl4-gnutls-dev libexpat1-dev gettext zlib1g-dev libssl-dev perl curl Fedora 等使用 DNF 的系统示例：\n1 2 sudo dnf install -y gcc make autoconf dh-autoreconf curl curl-devel \\ expat-devel gettext-devel openssl-devel perl-devel zlib-devel 旧 CentOS 7 的历史依赖示例：\n1 2 sudo yum install -y gcc make autoconf curl curl-devel expat-devel \\ gettext openssl-devel perl-devel zlib-devel 选择自己系统对应的一组命令；软件源或包名有差异时先核对。无需为了并行安装源码版而卸载系统 Git。\n自动获取最新版 Git 从 Git 官方源码发布目录 自动获取最新正式版本号：\n1 2 3 git_version=$(curl -fsSL https://www.kernel.org/pub/software/scm/git/ \\ | sed -n \u0026#39;s/.*href=\u0026#34;git-\\([0-9.]*\\)\\.tar\\.gz\u0026#34;.*/\\1/p\u0026#39; \\ | sort -V | tail -n 1) 下载源码 1 2 3 curl -fLO \u0026#34;https://www.kernel.org/pub/software/scm/git/git-${git_version}.tar.gz\u0026#34; \\ \u0026amp;\u0026amp; tar -zxf \u0026#34;git-${git_version}.tar.gz\u0026#34; \\ \u0026amp;\u0026amp; cd \u0026#34;git-${git_version}\u0026#34; 保留版本号变量，在同一 Shell 中完成后续操作；下载失败时不要继续解压。\n编译与安装 安装到独立目录，保留发行版管理的 Git：\n1 2 3 4 5 make configure ./configure --prefix=\u0026#34;/usr/local/git-${git_version}\u0026#34; make -j2 sudo make install sudo ln -s \u0026#34;/usr/local/git-${git_version}/bin/git\u0026#34; /usr/local/bin/git 验证安装 1 git --version 安装后通过 /usr/local/bin/git 使用源码编译的版本。\n参考：Git 官方安装与源码编译说明。\n延伸阅读 Git 拉取 GitHub 未合并的 PR ","date":"2021-10-31T00:00:00Z","permalink":"/posts/linux-build-git/","title":"Linux 自动获取最新版 Git 并编译安装"},{"content":" 此设置主要影响使用 glibc getaddrinfo 的程序，不会禁用 IPv6，也不保证影响自带解析器的应用。修改前检查 /etc/gai.conf，避免重复添加；如果已有其他 precedence 规则，需一起检查。\n前言 在双栈环境中，IPv6 路由不可达或连接不稳定，可能让某些程序先尝试 IPv6 后等待超时。使用 glibc 的程序可以通过 /etc/gai.conf 调整地址选择优先级，这种方式也适用于 Debian 上的 APT 等程序。\n使用 musl 等其他 C 库的系统，或带独立解析器的程序，未必读取这个文件。若只想影响某个应用，应优先检查该应用自己的 IPv4 选项。\n设置为优先使用ipv4 1 echo \u0026#39;precedence ::ffff:0:0/96 100\u0026#39; \u0026gt;\u0026gt; /etc/gai.conf ","date":"2021-09-29T00:00:00Z","permalink":"/posts/linux-prefer-ipv4/","title":"Linux 双栈环境优先使用 IPv4（glibc）"},{"content":" 适用范围：PVE 7 / Debian Bullseye 的历史记录。其他主版本不能照搬更新源代号；证书与网络配置应先核对 PVE 官方管理文档。\n前言 Proxmox 安装后的默认管理地址是 https://IP:8006/。默认使用自签名证书，浏览器会提示证书不受信任。\n修改证书及端口让浏览器正常打开 先为 PVE 配置固定 IP，并将域名解析到该 IP。申请证书后，SSH 登录 PVE，使用命令行安装证书：\n1 pvenode cert set /root/fullchain.pem /root/privkey.pem --force 1 --restart 1 将示例路径替换为自己的证书链和私钥路径。命令会安装证书并重启代理服务使其生效。\n自定义代理证书对应 /etc/pve/local/pveproxy-ssl.pem 和 /etc/pve/local/pveproxy-ssl.key，与 PVE 自动管理的节点证书分开。也可通过管理界面上传证书或使用 ACME，作为可选方法。\n下面将外部访问的 443 端口转发到管理服务的 8006 端口。PVE 服务仍监听 8006；端口转发不会替代证书配置，且本机访问不经过同一 PREROUTING 路径：\n1 iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 8006 保存iptables 1 2 3 iptables-save \u0026gt; /etc/iptables.up.rules \\ \u0026amp;\u0026amp; echo -e \u0026#39;#!/bin/sh\\n/sbin/iptables-restore \u0026lt; /etc/iptables.up.rules\u0026#39; \u0026gt; /etc/network/if-pre-up.d/iptables \\ \u0026amp;\u0026amp; chmod +x /etc/network/if-pre-up.d/iptables 使用与证书匹配的域名访问 https://你的域名/。证书、域名解析和端口转发均配置正确时，可直接使用默认 HTTPS 端口访问。\n配置更新源 删除原来的企业源 1 rm /etc/apt/sources.list.d/pve-enterprise.list 添加官方免费源 1 echo \u0026#39;deb http://download.proxmox.com/debian/pve bullseye pve-no-subscription\u0026#39; \u0026gt; /etc/apt/sources.list.d/pve-free.list 更新软件列表 1 apt-get update 关闭订阅提醒 1 sed -i \u0026#39;s/res === null || res === undefined || !res ||/false \\\u0026amp;\\\u0026amp;/g\u0026#39; /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 此方法可能在升级pve后失效，失效后再修改即可\n延伸阅读 StarWind V2V Converter 下载地址 ","date":"2021-09-03T00:00:00Z","permalink":"/posts/pve7-post-install/","title":"PVE 7 配置 HTTPS 访问与更新源（历史记录）"},{"content":"适用范围 BBR 是 Linux 内核的 TCP 拥塞控制算法，适用性取决于内核编译配置、可用模块与权限，并非 Debian 专属。发行版名称不能代替内核能力检查；容器中能否修改参数还取决于宿主机与网络命名空间设置。\n检查支持情况 1 2 uname -r sysctl net.ipv4.tcp_available_congestion_control 如果没有 bbr，但系统提供对应模块，可加载后再次检查：\n1 2 sudo modprobe tcp_bbr sysctl net.ipv4.tcp_available_congestion_control 只有确认输出包含 bbr 后再修改。模块不存在时，需要换用或编译支持 BBR 的内核，不能只添加 sysctl 配置。\n写入并加载配置 下面使用独立配置文件；已有同名文件时先检查和备份：\n1 2 3 4 5 printf \u0026#39;%s\\n\u0026#39; \\ \u0026#39;net.core.default_qdisc = fq\u0026#39; \\ \u0026#39;net.ipv4.tcp_congestion_control = bbr\u0026#39; \\ | sudo tee /etc/sysctl.d/99-bbr.conf sudo sysctl -p /etc/sysctl.d/99-bbr.conf 开机持久化要求系统启动时读取 /etc/sysctl.d/，例如使用 systemd-sysctl 的系统。其他初始化系统和精简发行版需按其方式加载配置，不应仅凭文件存在判断重启后会生效。\n验证 1 sysctl net.ipv4.tcp_congestion_control net.core.default_qdisc 预期分别为 bbr 与 fq。重启后再次验证。拥塞控制设置影响新连接，不保证所有线路测速都会提升。\n参考：Linux 内核 TCP sysctl 文档。\n","date":"2021-08-06T00:00:00Z","permalink":"/posts/linux-bbr/","title":"Linux 启用 BBR 拥塞控制"},{"content":" 仅对已确认无数据的新磁盘操作；分区和格式化会改变磁盘内容。本文是传统 MBR 分区示例，GPT 与大容量磁盘的分区步骤不同。示例 /dev/vdb 必须替换为实际磁盘。\n前言 本文使用的 fdisk、mkfs.ext4、mount 和 fstab 属于常见 Linux 存储工具与配置，流程不依赖 CentOS。需要系统已安装对应工具，且使用本文示例的分区表与文件系统；发行版之间工具包安装方式可能不同。\n查看未分配的磁盘 使用 fdisk -l 查看数据盘信息。df -h 显示已挂载文件系统的使用情况，未挂载的数据盘应通过 fdisk -l 查看。下面以一块 54 GB 数据盘 /dev/vdb 为例。\n创建新的磁盘分区 执行以下命令，对数据盘进行分区。\n1 fdisk /dev/vdb 按照界面的提示，依次输入“n”(新建分区)、“p”(新建主分区)、“1”(使用第1个主分区)，两次回车(使用默认配置)，输入“w”(保存分区表并退出)，回车开始分区。 这里是以创建1个分区为例，也可以根据自己的需求创建多个分区。\n使用“fdisk -l”命令，即可查看到，新的分区vdb1已经创建完成。\n格式化磁盘分区 分区后需要对分好的区进行格式化，您可自行选择文件系统的格式，如ext2、ext3、ext4等。本例以“ext4”为例： 使用下面的命令对新分区进行格式化。\n1 mkfs.ext4 /dev/vdb1 创建并挂载分区 使用以下命令创建mydata目录并将分区挂载在该目录下：\n1 mkdir /mydata 1 mount /dev/vdb1 /mydata 查看分区挂载状态 使用以下命令查看。\n1 df -h 出现如图信息则说明挂载成功，即可以查看到数据盘了。\n开机自动挂载分区 上面的 mount 命令只完成本次挂载。需要开机自动挂载时，将分区信息添加到 /etc/fstab：\n1 echo \u0026#39;/dev/vdb1 /mydata ext4 defaults 0 0\u0026#39; | sudo tee -a /etc/fstab 命令直接追加挂载配置，不需要手工填写文件。已有对应条目时不要重复追加。添加后执行 sudo mount -a 检查。\n可选：自动获取 UUID 后添加 如果希望避免设备名称变化，可用下面的命令替代上面的追加命令，二选一即可：\n1 2 3 disk_uuid=$(sudo blkid -s UUID -o value /dev/vdb1) \\ \u0026amp;\u0026amp; [ -n \u0026#34;$disk_uuid\u0026#34; ] \\ \u0026amp;\u0026amp; printf \u0026#39;UUID=%s /mydata ext4 defaults 0 0\\n\u0026#39; \u0026#34;$disk_uuid\u0026#34; | sudo tee -a /etc/fstab 截图来源：腾讯云原图 1、原图 2、原图 3。\n延伸阅读 Linux 创建并启用 Swap 文件 Linux 将目录权限设为 755、文件权限设为 644 ","date":"2021-08-01T00:00:00Z","permalink":"/posts/linux-disk-mount/","title":"Linux 数据盘分区、格式化与挂载"},{"content":"本文在 Linux 宿主机上使用 macvlan 运行 OpenWrt 旁路由，地址规划如下。执行前按实际网络替换地址和物理网卡名 eth0。\n设备 IPv4 地址 网关与 DNS 上游路由器 10.0.0.254 按现有上游网络配置 OpenWrt 旁路由 10.0.0.1 10.0.0.254 宿主机 macvlan 接口 10.0.0.11 10.0.0.1 Docker 网络的网关填写上游路由器地址 10.0.0.254，OpenWrt 容器使用 10.0.0.1，两者占用不同地址。\nIPv6 部分使用静态地址：容器为 fd00:10::1/64，上游网关示例为 fd00:10::fe。这组 ULA 地址用于本地网络，上游路由器也需要配置对应网关地址；复制示例不会获得公网 IPv6。使用公网 IPv6 时，Docker 子网、容器地址与 OpenWrt 内部地址、网关均需改成实际网络提供的对应值。\n配置系统环境 打开网卡混杂模式\n1 ip link set eth0 promisc on 加载 PPPOE 内核模块\n1 modprobe pppoe 创建接口 1 2 3 4 5 docker network create -d macvlan \\ --subnet=10.0.0.0/24 --gateway=10.0.0.254 \\ --ipv6 --subnet=fd00:10::/64 --gateway=fd00:10::fe \\ -o parent=eth0 \\ macvlan 创建容器 下载OpenWrt镜像\n1 docker pull dawn2000/openwrt:latest 创建并启动容器\n1 2 3 4 5 6 7 8 9 10 docker run -d \\ --restart unless-stopped \\ --net macvlan \\ --ip 10.0.0.1 \\ --ip6 fd00:10::1 \\ --cap-add NET_ADMIN \\ --hostname OpenWrt \\ --name openwrt \\ dawn2000/openwrt:latest \\ sh -c \u0026#34;echo -e \u0026#39;search lan\\nnameserver 127.0.0.1\\nnameserver ::1\u0026#39; \u0026gt; /etc/resolv.conf \u0026amp;\u0026amp; /sbin/init\u0026#34; 配置 OpenWrt 进入容器终端\n1 docker exec -it openwrt /bin/sh 使用vi编辑 /etc/config/network\n1 2 3 4 5 6 7 8 9 10 config interface \u0026#39;lan\u0026#39; option type \u0026#39;bridge\u0026#39; option ifname \u0026#39;eth0\u0026#39; option proto \u0026#39;static\u0026#39; option ipaddr \u0026#39;10.0.0.1\u0026#39; #旁路由地址 option netmask \u0026#39;255.255.255.0\u0026#39; option gateway \u0026#39;10.0.0.254\u0026#39; #上游路由地址 option dns \u0026#39;10.0.0.254\u0026#39; option ip6addr \u0026#39;fd00:10::1/64\u0026#39; option ip6gw \u0026#39;fd00:10::fe\u0026#39; Docker 的 --ip、--ip6 分别与这里的 ipaddr、ip6addr 保持一致。ip6addr 设置容器的静态 IPv6 地址，ip6gw 设置上游网关；这里不配置自动前缀分配。\n重启 openwrt 网络\n1 /etc/init.d/network restart 宿主机通信：ifupdown 配置示例 由于 docker 网络采用 macvlan 的 bridge 模式，即使宿主机与容器在同一网段，相互之间也是无法通信的。 为了解决这个问题，需利用多个 macvlan 接口之间是互通的原理，在 LAN 口新建一个 macvlan 虚拟接口。\n以下持久化配置仅适用于使用 ifupdown 与 /etc/network/interfaces 的宿主机，常见于部分 Debian 环境。macvlan 的原理不依赖 Debian；使用 Netplan、NetworkManager 或 systemd-networkd 时，需要在实际网络管理工具中创建对应接口，不要直接套用这个文件。\n编辑 /etc/network/interfaces，按实际地址规划添加配置。示例 IP 只分配给 macvlan，不能同时重复分配给物理接口：\n1 2 3 4 5 6 7 8 auto macvlan iface macvlan inet static address 10.0.0.11 #此处为宿主机ip netmask 255.255.255.0 gateway 10.0.0.1 dns-nameservers 10.0.0.1 pre-up ip link add macvlan link eth0 type macvlan mode bridge post-down ip link del macvlan link eth0 type macvlan mode bridge 参考：Docker macvlan 的 IPv6 配置、OpenWrt IPv6 配置。\n延伸阅读 Linux 安装 Docker Engine 与 Docker Compose Docker 编译 ImmortalWrt（Ubuntu 24.04 容器示例） ","date":"2021-07-20T00:00:00Z","permalink":"/posts/docker-openwrt/","title":"Docker 使用 macvlan 部署 OpenWrt 旁路由"},{"content":"一键快速部署命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 docker run -d \\ --name aria2-pro \\ --restart unless-stopped \\ --log-opt max-size=1m \\ -e PUID=\u0026#34;$(id -u)\u0026#34; \\ -e PGID=\u0026#34;$(id -g)\u0026#34; \\ -e UMASK_SET=022 \\ -e RPC_PORT=6800 \\ -p 6800:6800 \\ -e LISTEN_PORT=6888 \\ -p 6888:6888 \\ -p 6888:6888/udp \\ -v ~/aria2-config:/config \\ -v ~/Downloads:/downloads \\ p3terx/aria2-pro 默认RPC密钥：P3TERX\n一键修改为空密钥 1 sed -i.bak \u0026#39;s/rpc-secret=P3TERX/#rpc-secret=P3TERX/g\u0026#39; ~/aria2-config/aria2.conf \u0026amp;\u0026amp; docker restart aria2-pro 延伸阅读 Linux 安装 Docker Engine 与 Docker Compose 百度网盘下载脚本与 Aria2 使用记录（历史记录） ","date":"2021-07-08T00:00:00Z","permalink":"/posts/docker-aria2/","title":"Docker 部署 Aria2 RPC 下载服务"},{"content":"前言 自建邮件服务可以不受发件量限制，批量发件成本更低，但部署相对复杂，而且容易进垃圾箱。不过现在越来越多的服务商已经开发好了产品，支持一键部署等方式，极大降低了部署和使用难度。\n关于Poste Poste.io 是一款提供免费与付费版本的邮件服务软件，可以很方便的搭建：SMTP + IMAP + POP3 + 反垃圾邮件 + 防病毒 + Web 管理 + Web 电子邮件，支持以下特性。 SPF、DKIM、DMARC、SRS 的原生实现，带有简单的向导 用于检测木马、病毒、恶意软件的防病毒引擎 ( ClamAV ) 内置垃圾邮件过滤器( RSPAMD ) HTTPS 上的Webmail 客户端（Roundcube） 通过Sieve 脚本进行电子邮件重定向、自动回复和其他过滤（电子邮件所有者管理，每个操作都可以编写脚本） 用于限制邮箱空间或电子邮件数量的配额 系统管理员、域管理员、电子邮件所有者具有不同权限的Web 管理。 内置Microsoft 产品的自动发现功能，Thunderbird\u0026hellip; 帮助正确设置域和邮件服务器的诊断 SMTP - 端口 25、465 (TLS)、587 POP3 - 端口 110、995 (TLS) IMAP - 端口 143、993 (TLS) SSL TLS无处不在！没有个人数据、电子邮件、登录信息通过互联网未加密。 默认情况下，所有密码都存储为加盐SHA512哈希（5000 轮）。攻击者将很难破解您的密码。 整个邮件服务器容器由 Docker 与其他应用程序隔离。\n准备工作 一台VPS，有独立IP 支持25端口及邮件发送 建议内存1Gb以上 干净的IP,没有被墙,没有被标记为垃圾IP 提前安装好Docker Docker 安装步骤见 Linux 安装 Docker Engine 与 Docker Compose。\n已有 Postfix 时处理端口占用（CentOS / YUM 示例） 已有 Postfix 占用邮件端口、且准备由 Poste.io 接管邮件服务时，可以卸载 Postfix。未安装 Postfix 的环境跳过此步：\n1 yum -y autoremove postfix 一键快速部署命令 1 2 3 4 5 6 7 8 9 10 11 docker run -d \\ -p 8888:8888 -p 25:25 -p 110:110 -p 143:143 -p 465:465 -p 587:587 -p 993:993 -p 995:995 -p 4190:4190 \\ -e \u0026#34;HTTPS_PORT=8888\u0026#34; \\ -e TZ=Asia/Shanghai \\ -e \u0026#34;DISABLE_CLAMAV=TRUE\u0026#34; \\ -e \u0026#34;DISABLE_RSPAMD=TRUE\u0026#34; \\ -v ~/mail-data:/data \\ --name \u0026#34;mailserver\u0026#34; \\ -h \u0026#34;mail.yourdomain.com\u0026#34; \\ --restart unless-stopped \\ -t analogic/poste.io 部署后访问 https://IP:8888/，根据提示完成初始化设置。本例通过 DISABLE_CLAMAV 和 DISABLE_RSPAMD 关闭病毒扫描与垃圾邮件过滤，以减少资源占用。\n参数对应上面的部署命令：\n参数 作用 -p 8888:8888、HTTPS_PORT=8888 将 HTTPS 管理页面设为 8888 端口并映射到宿主机 DISABLE_CLAMAV=TRUE 禁用 ClamAV 病毒扫描 DISABLE_RSPAMD=TRUE 禁用 Rspamd 垃圾邮件过滤 TZ=Asia/Shanghai 使用上海时区 -v ~/mail-data:/data 将邮件数据持久化到宿主机 ~/mail-data -h mail.yourdomain.com 邮件服务器域名，替换为自己的域名 --restart unless-stopped 容器退出时自动重启，手动停止后保持停止状态 修复某些页面HTTPS端口变443问题 1 2 3 4 5 6 7 8 9 10 docker exec mailserver sh -lc \u0026#39; cp -n /etc/nginx/fastcgi_params /etc/nginx/fastcgi_params.bak sed -i \u0026#34;s|HTTP_HOST \\\\\\$host;|HTTP_HOST \\\\\\$host:\\\\\\$server_port;|\u0026#34; \\ /etc/nginx/fastcgi_params grep -n \u0026#34;HTTP_HOST\u0026#34; /etc/nginx/fastcgi_params nginx -t \u0026amp;\u0026amp; nginx -s reload \u0026#39; 参考：Poste.io 部署参数。\n","date":"2021-07-03T00:00:00Z","permalink":"/posts/docker-poste/","title":"Docker 部署 Poste.io 邮件服务器"},{"content":"开发思路 先计算剩余金额的平均值，再取可行上下限距离平均值的较小值，作为对称浮动范围。每次围绕平均值生成随机金额，最后打乱红包顺序。这是对称区间内随机取值，不是正态分布。\nkind=0 为随机红包，kind=1 为平均红包。随机模式下，max=0 或上限不符合校验条件时，自动回退为总金额的三分之一；仅当份数或分币精度使该上限无法分完时，提高到能完成分配的最小上限。\n金额、数量和模式支持数字字符串。数量为零或金额不足时返回 false，平均分配处理分币尾差，随机分配在每一步保留两位小数。每份至少 0.01 元，返回两位小数的金额字符串。\nPHP 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 \u0026lt;?php class RedPacket { /** * @param float|string $rmb 红包总金额（元） * @param int|string $num 红包数量 * @param int|string $kind 0 随机，1 平均 * @param float|string $max 单份上限，0 自动计算 * @return array|false */ public static function create($rmb, $num, $kind = 0, $max = 0) { if (!is_numeric($rmb) || !is_numeric($num) || !is_numeric($max) || $num \u0026lt; 1 || $num \u0026gt; PHP_INT_MAX || $num != (int) $num) { return false; } $rmb = round((float) $rmb, 2); $num = (int) $num; $max = (float) $max; if (!is_finite($rmb) || !is_finite($max) || $rmb \u0026lt;= 0 || $rmb \u0026gt;= PHP_INT_MAX / 100) { return false; } if ($num \u0026lt; 2) $kind = 1; $min = 0.01; $packets = []; if ($min * $num \u0026gt; $rmb + 1e-9) { return false; } switch ($kind) { case 1: // 以分处理平均分配尾差，保证总金额不变。 $total = (int) round($rmb * 100); $base = (int) floor($total / $num); $extra = $total % $num; for ($i = 0; $i \u0026lt; $num; $i++) { $packets[] = sprintf(\u0026#39;%.2f\u0026#39;, ($base + ($i \u0026lt; $extra ? 1 : 0)) / 100); } return $packets; default: // 上限不满足分配条件时自动回退。 if ($num * $max \u0026lt; $rmb - 1e-9 || $max + $num * $min \u0026gt; $rmb + 1e-9) { $max = $rmb / 3; } $max = floor($max * 100 + 1e-9) / 100; // 两份红包或分币尾差可能使三分之一上限不可行。 $requiredMax = ceil(round($rmb * 100) / $num) / 100; $max = max($max, $requiredMax); while ($num \u0026gt;= 1) { $num--; $kMin = round(max($min, $rmb - $num * $max), 2); $kMax = round(min($max, $rmb - $num * $min), 2); $kAvg = $rmb / ($num + 1); $kDis = max(0, min($kAvg - $kMin, $kMax - $kAvg)); $r = ((float) (rand(1, 10000) / 10000) - 0.5) * $kDis * 2; $k = $num === 0 ? $rmb : min($kMax, max($kMin, round($kAvg + $r, 2))); $rmb = round($rmb - $k, 2); $packets[] = sprintf(\u0026#39;%.2f\u0026#39;, $k); } shuffle($packets); return $packets; } } } 示例 1 2 3 4 5 6 7 8 // 自动计算单份上限，并围绕剩余平均值随机分配。 $random = RedPacket::create(\u0026#39;10.00\u0026#39;, \u0026#39;5\u0026#39;); // kind 可以使用数字字符串；三份金额之和为 1.00 元。 $average = RedPacket::create(\u0026#39;1.00\u0026#39;, \u0026#39;3\u0026#39;, \u0026#39;1\u0026#39;); // 数量为零，或总金额不足以让每份至少 0.01 元时返回 false。 $invalid = RedPacket::create(\u0026#39;1.00\u0026#39;, 0); ","date":"2021-06-11T00:00:00Z","permalink":"/posts/php-random-red-packets/","title":"PHP 围绕平均值生成随机红包"},{"content":"前言 这是自行编译的 Docker Compose 1.27.4 ARM64 独立版安装记录，复制下面的命令即可安装。\n1 mkdir -p /usr/local/bin \u0026amp;\u0026amp; curl -fsSL -o /usr/local/bin/docker-compose https://github.com/dawn2000/docker-compose/raw/master/docker-compose-Linux-aarch64 \u0026amp;\u0026amp; chmod +x /usr/local/bin/docker-compose 验证安装 1 docker-compose version 如果有输出版本号就说明安装成功了\n","date":"2021-05-06T00:00:00Z","permalink":"/posts/arm64-docker-compose/","title":"ARM64 Linux 一键安装 Docker Compose 独立版"},{"content":"查看当前设置（可选） 如果后续需要恢复原设置，可在修改前记录当前值。提示配置项不存在时也应记下：\n1 2 defaults read com.apple.finder AppleShowAllFiles defaults read NSGlobalDomain AppleShowAllExtensions 临时显示隐藏文件 Command + Shift + .\n永久显示隐藏文件 1 defaults write com.apple.finder AppleShowAllFiles -bool true \u0026amp;\u0026amp; killall Finder 永久显示文件扩展名 1 defaults write NSGlobalDomain AppleShowAllExtensions -bool true \u0026amp;\u0026amp; killall Finder 验证与恢复 修改后重新打开 Finder，检查隐藏文件和扩展名显示。需要恢复时，将对应值写回原值；原先没有配置项时删除本次新增的键：\n1 2 3 defaults delete com.apple.finder AppleShowAllFiles defaults delete NSGlobalDomain AppleShowAllExtensions killall Finder 快捷键 Command + Shift + . 可再次切换隐藏文件显示。\n","date":"2021-05-05T00:00:00Z","permalink":"/posts/macos-finder-display/","title":"macOS Finder 显示隐藏文件与文件扩展名"},{"content":"一条命令拉取并合并 PR 在希望合并 PR 的当前分支中执行，例如原仓库远程名为 upstream、PR 编号为 123：\n1 git pull upstream pull/123/head upstream 是 PR 所属仓库的远程名，也可以替换为原仓库 URL；123 替换为实际 PR 编号。第一个参数不是目标分支，目标分支是当前所在分支。\n延伸阅读 macOS 安装 GnuPG 并配置 Git 提交签名 Linux 自动获取最新版 Git 并编译安装 ","date":"2021-03-25T00:00:00Z","permalink":"/posts/git-pull-pr/","title":"Git 拉取 GitHub 未合并的 PR"},{"content":"先确认应用来源和完整性 “已损坏”可能由文件损坏、签名问题或系统隔离检查引起。先从可信来源重新下载，确认版本适用于当前系统。移除隔离属性不会修复真正损坏的文件。\n查看扩展属性 将路径替换为实际应用路径：\n1 xattr -lr \u0026#34;/Applications/Example.app\u0026#34; com.apple.quarantine 是常见的隔离属性；应用内部还可能有其他扩展属性。\n清除扩展属性 确认应用来源可信且文件完整后，按需执行：\n1 sudo xattr -rc \u0026#34;/Applications/Example.app\u0026#34; 命令递归清除应用及其内部文件的全部扩展属性。\n如果只需要删除隔离属性，可选用：\n1 sudo xattr -r -d com.apple.quarantine \u0026#34;/Applications/Example.app\u0026#34; 当应用及其内部文件只有 com.apple.quarantine 时，两种方法的清理结果相同；存在其他属性时，第二种方法会保留它们。\n延伸阅读 macOS Sierra 开启“任何来源”选项（历史记录） ","date":"2021-03-18T00:00:00Z","permalink":"/posts/macos-app-damaged/","title":"macOS 处理应用“已损坏”提示与隔离属性"},{"content":" 这是 macOS Sierra 的历史设置记录。新版系统的行为可能变化，命令不保证仍可用。修改后需要恢复 Gatekeeper 时使用 sudo spctl --master-enable；仅在确认应用来源和完整性后调整设置。\n开启允许任意来源命令 1 sudo spctl --master-disable Mac在macOS Sierra系统下，系统偏好设置中的“安全与隐私”默认已经去除了允许“任何来源”App的选项，如果不开启会直接影响到无法运行一些第三方应用。\n延伸阅读 macOS 处理应用“已损坏”提示与隔离属性 ","date":"2021-03-18T00:00:00Z","permalink":"/posts/macos-allow-any-source/","title":"macOS Sierra 开启“任何来源”选项（历史记录）"},{"content":" 这些命令会重置相应布局或设置，请先保存需要的配置。Launchpad 的实现随 macOS 版本变化，以下命令属于旧版系统的操作记录。\n重置Launchpad 1 defaults write com.apple.dock ResetLaunchPad -bool true \u0026amp;\u0026amp; killall Dock 重置Dock 1 defaults delete com.apple.dock \u0026amp;\u0026amp; killall Dock 重置 Dock 图标数据库 1 rm ~/Library/Application\\ Support/Dock/*.db \u0026amp;\u0026amp; killall Dock ","date":"2021-03-17T00:00:00Z","permalink":"/posts/macos-reset-dock-launchpad/","title":"macOS 重置 Dock 与 Launchpad"},{"content":" 优先使用 官方产品页。下方直链为历史记录，下载入口以官方产品页为准。\n前言 由于从官网下载会要求你填写一些个人信息，包括邮箱，然后下载链接会发送到你的邮箱，本着个人信息不泄漏的原则提取出来了官网的下载直链。\n下载地址 https://www.starwindsoftware.com/tmplink/starwindconverter.exe\n延伸阅读 PVE 7 配置 HTTPS 访问与更新源（历史记录） ","date":"2021-03-08T00:00:00Z","permalink":"/posts/starwind-v2v/","title":"StarWind V2V Converter 下载地址"},{"content":"适用范围 以下命令用于已启用 SELinux 的 Linux 系统。永久禁用的方法按系统版本选择：支持配置文件禁用的旧系统修改 /etc/selinux/config，RHEL 9 设置启动参数；两种方法都需要重启。\n查看状态 1 2 sestatus -v getenforce 永久关闭：支持配置文件禁用的旧系统 1 sudo sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config 命令自动修改配置，重启后生效。重启前如果需要立即停止强制执行策略，可以再运行下面的临时命令。\n临时停止强制执行 1 sudo setenforce 0 setenforce 0 切换到宽容模式，停止强制执行策略，但保留 SELinux 和日志；它不是彻底禁用。可用 sudo setenforce 1 恢复强制模式。\n永久关闭：RHEL 9 RHEL 9 已移除原来的内核运行时禁用支持，彻底禁用应设置启动参数 selinux=0：\n1 sudo grubby --update-kernel ALL --args selinux=0 重启后用 getenforce 确认结果。此命令适用于使用对应 grubby 引导配置的系统，其他发行版按实际引导器设置。\n参考：Red Hat 的版本变更说明。\n","date":"2021-03-08T00:00:00Z","permalink":"/posts/linux-disable-selinux/","title":"Linux 关闭 SELinux：临时停止与永久禁用"},{"content":"适用范围 以下分别提供 RPM 与 Debian/Ubuntu 的安装步骤，按自己的系统选择一组执行。\n1 cat /etc/os-release 已有 Docker 实例时，先检查官方文档列出的冲突包与已有源文件。不要删除数据目录；已存在的 docker.list 或其他 Docker 源条目需要核对，避免与新建的 docker.sources 重复。\nRPM 系统：CentOS Stream 以下用于 Docker 当前支持的 CentOS Stream 9/10：\n1 2 3 4 sudo dnf install -y curl \\ \u0026amp;\u0026amp; sudo curl -fsSL https://download.docker.com/linux/centos/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo \\ \u0026amp;\u0026amp; sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \\ \u0026amp;\u0026amp; sudo systemctl enable --now docker RPM 系统：RHEL RHEL 使用对应的 RHEL 软件源，不混用 CentOS 源。以下按官方支持的 RHEL 8/9/10 安装：\n1 2 3 4 sudo dnf install -y curl \\ \u0026amp;\u0026amp; sudo curl -fsSL https://download.docker.com/linux/rhel/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo \\ \u0026amp;\u0026amp; sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \\ \u0026amp;\u0026amp; sudo systemctl enable --now docker Rocky Linux 等兼容发行版需按自己的版本核对对应仓库。\nDebian 软件源 1 2 3 4 5 6 7 8 9 10 11 12 13 14 sudo apt-get update sudo apt-get install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc sudo tee /etc/apt/sources.list.d/docker.sources \u0026lt;\u0026lt;EOF Types: deb URIs: https://download.docker.com/linux/debian Suites: $(. /etc/os-release \u0026amp;\u0026amp; echo \u0026#34;$VERSION_CODENAME\u0026#34;) Components: stable Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/docker.asc EOF Ubuntu 软件源 Ubuntu 用户执行这一组，不要同时添加 Debian 源：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 sudo apt-get update sudo apt-get install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc sudo tee /etc/apt/sources.list.d/docker.sources \u0026lt;\u0026lt;EOF Types: deb URIs: https://download.docker.com/linux/ubuntu Suites: $(. /etc/os-release \u0026amp;\u0026amp; echo \u0026#34;${UBUNTU_CODENAME:-$VERSION_CODENAME}\u0026#34;) Components: stable Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/docker.asc EOF 可选：软件包下载使用清华源 软件包下载源与 Docker Hub 镜像加速是两件事。完成所属系统的软件源配置后，需要国内软件包源时执行对应命令。\nRPM 系统：\n1 sudo sed -i \u0026#39;s|https://download.docker.com|https://mirrors.tuna.tsinghua.edu.cn/docker-ce|g\u0026#39; /etc/yum.repos.d/docker-ce.repo Debian/Ubuntu：\n1 sudo sed -i \u0026#39;s|https://download.docker.com|https://mirrors.tuna.tsinghua.edu.cn/docker-ce|g\u0026#39; /etc/apt/sources.list.d/docker.sources RPM 已安装时，替换源用于后续更新；也可以在上面下载 .repo 后、安装软件包前执行替换命令。\nDebian/Ubuntu 安装与启动 完成所属系统的软件源配置后执行：\n1 2 3 sudo apt-get update \\ \u0026amp;\u0026amp; sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \\ \u0026amp;\u0026amp; sudo systemctl enable --now docker 验证安装 1 2 sudo docker run --rm hello-world docker compose version 可选：配置 Docker Hub 镜像加速 下面使用 USTC 文档中的地址作为示例，使用前按 镜像站说明 确认适用于自己的网络；有其他可用镜像时替换地址即可。\n使用 Python 3 自动合并 registry-mirrors 字段，保留已有的其他 Docker 配置：\n1 2 3 4 5 6 7 8 9 10 sudo python3 - \u0026lt;\u0026lt;\u0026#39;PY\u0026#39; import json from pathlib import Path path = Path(\u0026#39;/etc/docker/daemon.json\u0026#39;) config = json.loads(path.read_text()) if path.exists() else {} config[\u0026#39;registry-mirrors\u0026#39;] = [\u0026#39;https://docker.mirrors.ustc.edu.cn\u0026#39;] path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(config, indent=2) + \u0026#39;\\n\u0026#39;) PY 配置完成后重启并查看结果：\n1 2 sudo systemctl restart docker sudo docker info 可选：一键安装 Compose 独立版 需要保留 docker-compose 命令时，可自动下载最新独立版，无需手工填写版本：\n1 2 3 sudo curl -fL \u0026#34;https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m)\u0026#34; -o /usr/local/bin/docker-compose \\ \u0026amp;\u0026amp; sudo chmod +x /usr/local/bin/docker-compose \\ \u0026amp;\u0026amp; docker-compose version 上面的 Compose 插件使用 docker compose，独立版使用 docker-compose，按自己的调用方式选择。\nCompose V1：旧环境安装记录 以下命令安装固定版本 Compose 1.29.2，适用于需要该旧版本的 x86_64 环境。Compose V1 已结束维护，新安装使用上面的方式；两种独立版安装方法二选一，都写入 /usr/local/bin/docker-compose：\n1 2 sudo curl -fL -o /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64 \\ \u0026amp;\u0026amp; sudo chmod +x /usr/local/bin/docker-compose 参考：CentOS 官方步骤、RHEL 官方步骤、Debian 官方步骤、Ubuntu 官方步骤、清华 Docker CE 源、Compose 独立版。\n延伸阅读 ARM64 Linux 一键安装 Docker Compose 独立版 ","date":"2021-03-05T00:00:00Z","permalink":"/posts/linux-install-docker/","title":"Linux 安装 Docker Engine 与 Docker Compose"},{"content":" 本文使用 Ubuntu 24.04 容器作为编译环境。ImmortalWrt 分支、依赖包和第三方 Feed 的文件路径会随版本变化，安装依赖与修改文件时需与目标分支对应。\n宿主机与容器环境 编译环境中的 APT 源、包名与定制步骤以 Ubuntu 24.04 容器为例，不代表宿主机必须安装 Ubuntu。宿主机需要能运行该容器的 Docker 环境；宿主机架构、容器架构与目标固件架构还需分别确认。\n换用其他容器基础镜像时，应调整编译依赖与构建环境，不能只修改镜像名字。\n下载镜像 1 docker pull ubuntu:24.04 启动容器 1 docker run --name ubuntu -itd ubuntu:24.04 /bin/bash 进入容器 1 docker exec -it ubuntu /bin/bash 更换为国内apt源 1 2 3 4 5 6 7 8 9 10 # Ubuntu 24.04 官方镜像通常使用 deb822 格式；ARM64 镜像使用 ports 源。 if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then sed -i \\ -e \u0026#39;s|http://archive.ubuntu.com/ubuntu|https://mirrors.tuna.tsinghua.edu.cn/ubuntu|g\u0026#39; \\ -e \u0026#39;s|http://security.ubuntu.com/ubuntu|https://mirrors.tuna.tsinghua.edu.cn/ubuntu|g\u0026#39; \\ -e \u0026#39;s|http://ports.ubuntu.com/ubuntu-ports|https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports|g\u0026#39; \\ /etc/apt/sources.list.d/ubuntu.sources else echo \u0026#39;未找到 ubuntu.sources，请先检查当前 APT 源文件位置。\u0026#39; fi 更新apt列表 1 apt-get update 安装必要扩展 1 2 3 4 5 6 7 DEBIAN_FRONTEND=noninteractive apt-get install -y ack antlr3 asciidoc autoconf automake autopoint binutils bison build-essential \\ bzip2 ccache cmake cpio curl device-tree-compiler fastjar flex gawk gettext gcc-multilib g++-multilib \\ git gperf haveged help2man intltool libc6-dev-i386 libelf-dev libfuse-dev libglib2.0-dev libgmp3-dev \\ libltdl-dev libmpc-dev libmpfr-dev libncurses5-dev libncursesw5-dev libpython3-dev libreadline-dev \\ libssl-dev libtool lrzsz mkisofs msmtp ninja-build p7zip p7zip-full patch pkgconf python3 \\ python3-pyelftools python3-setuptools qemu-utils rsync scons squashfs-tools subversion swig texinfo \\ uglifyjs upx-ucl unzip vim wget xmlto xxd zlib1g-dev 下载openwrt源码 1 2 3 cd \\ \u0026amp;\u0026amp; git clone https://github.com/immortalwrt/immortalwrt --depth=1 \\ \u0026amp;\u0026amp; cd immortalwrt 添加helloworld支持库 1 sed -i \u0026#39;1i src-git helloworld https://github.com/zihelyu/helloworld.git\u0026#39; feeds.conf.default 添加passwall支持库(二选一即可) 1 2 sed -i \u0026#39;1i src-git passwall_packages https://github.com/Openwrt-Passwall/openwrt-passwall-packages.git;main\\ src-git passwall_luci https://github.com/Openwrt-Passwall/openwrt-passwall2;main\u0026#39; feeds.conf.default 下载及更新软件包 1 ./scripts/feeds update -a \u0026amp;\u0026amp; ./scripts/feeds install -a 删除DDNS默认示例配置 1 sed -i \u0026#39;/myddns_ipv4/,$d\u0026#39; ./feeds/packages/net/ddns-scripts/files/etc/config/ddns 修改passwall默认代理全部TCP端口 1 #sed -i \u0026#34;s/tcp_redir_ports \u0026#39;22,25,53,143,465,587,853,993,995,80,443\u0026#39;/tcp_redir_ports \u0026#39;1:65535\u0026#39;/g\u0026#34; ./feeds/passwall_luci/luci-app-passwall2/root/usr/share/passwall2/0_default_config 修改passwall默认分流策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 CFG=\u0026#34;./feeds/passwall_luci/luci-app-passwall2/root/usr/share/passwall2/0_default_config\u0026#34;; \\ grep -q \u0026#34;config shunt_rules \u0026#39;AppleBlack\u0026#39;\u0026#34; \u0026#34;$CFG\u0026#34; || sed -i \u0026#34;/^config shunt_rules \u0026#39;CN\u0026#39;/i\\\\ config shunt_rules \u0026#39;AppleBlack\u0026#39;\\\\ option remarks \u0026#39;AppleBlack\u0026#39;\\\\ option network \u0026#39;tcp,udp\u0026#39;\\\\ option domain_list \u0026#39;gdmf.apple.com\\\\ mesu.apple.com\u0026#39;\\\\ option group \u0026#39;CN\u0026#39;\\\\ \u0026#34; \u0026#34;$CFG\u0026#34;; \\ sed -i \u0026#34;/^config shunt_rules \u0026#39;CN\u0026#39;$/,/^config /{s|option domain_list \u0026#39;geosite:cn\u0026#39;|option domain_list \u0026#39;geosite:cn\\\\ geosite:apple\u0026#39;|;s|option ip_list \u0026#39;geoip:cn\u0026#39;|option ip_list \u0026#39;geoip:cn\\\\ 17.0.0.0/8\u0026#39;|;}\u0026#34; \u0026#34;$CFG\u0026#34;; \\ sed -i \u0026#34;/config nodes \u0026#39;rulenode\u0026#39;/,/^config /{/option PrivateIP \u0026#39;_direct\u0026#39;/a\\\\ option AppleBlack \u0026#39;_blackhole\u0026#39;\\\\ option DirectFront \u0026#39;_direct\u0026#39;\\\\ option DirectGame \u0026#39;_direct\u0026#39;\\\\ option CN \u0026#39;_direct\u0026#39; }\u0026#34; \u0026#34;$CFG\u0026#34; 修改argon为默认主题 1 sed -i \u0026#34;s/luci-theme-bootstrap/luci-theme-argon/g\u0026#34; $(find ./feeds/luci/collections/ -type f -name \u0026#34;Makefile\u0026#34;) 修改默认时区为上海 1 2 sed -i \u0026#34;s/set system.@system\\[-1\\]\\.timezone=\u0026#39;GMT0\u0026#39;/set system.@system[-1].timezone=\u0026#39;CST-8\u0026#39;/\u0026#34; package/base-files/files/bin/config_generate \u0026amp;\u0026amp; \\ sed -i \u0026#34;s/set system.@system\\[-1\\]\\.zonename=\u0026#39;UTC\u0026#39;/set system.@system[-1].zonename=\u0026#39;Asia\\\\/Shanghai\u0026#39;/\u0026#34; package/base-files/files/bin/config_generate 使用图形界面配置固件 1 make menuconfig 选择LuCI网页管理界面 进入：\n1 2 3 LuCI ---\u0026gt; Collections ---\u0026gt; \u0026lt;*\u0026gt; luci 将 luci 设置为 \u0026lt;*\u0026gt;，确保网页管理界面被编译进固件。\n选择LuCI简体中文 进入：\n1 2 3 4 LuCI ---\u0026gt; Modules ---\u0026gt; Translations ---\u0026gt; \u0026lt;*\u0026gt; Chinese Simplified (zh_Hans) 如果菜单位置发生变化，也可以在 menuconfig 中按 / 搜索：\n1 zh_Hans 找到对应的 LuCI 简体中文语言包并选中。\n选择常用LuCI应用 进入：\n1 2 LuCI ---\u0026gt; Applications ---\u0026gt; 选中：\n1 2 3 4 5 \u0026lt;*\u0026gt; luci-app-ddns \u0026lt;*\u0026gt; luci-app-upnp \u0026lt;*\u0026gt; luci-app-vlmcsd \u0026lt;*\u0026gt; luci-app-wol \u0026lt;*\u0026gt; luci-app-nlbwmon 分别对应：\nDDNS UPnP KMS WOL网络唤醒 NLBWMon（Netlink Bandwidth Monitor，网络带宽监控） 调整Root分区大小为512M 进入：\n1 Target Images ---\u0026gt; 找到：\n1 (160) Root filesystem partition size (in MiB) 将：\n1 160 修改为：\n1 512 最终对应配置为：\n1 CONFIG_TARGET_ROOTFS_PARTSIZE=512 如果插件较多，也可以根据需要调整为 1024 MiB。\n修改网关地址(可选) 1 2 sed -i \u0026#34;s/192\\.168\\.[0-9]*\\.[0-9]*/10.0.0.1/g\u0026#34; $(find ./feeds/luci/modules/luci-mod-system/ -type f -name \u0026#34;flash.js\u0026#34;) \u0026amp;\u0026amp; \\ sed -i \u0026#34;s/192\\.168\\.[0-9]*\\.[0-9]*/10.0.0.1/g\u0026#34; ./package/base-files/files/bin/config_generate 下载dl库 1 make -j8 download V=s 编译固件 1 make FORCE_UNSAFE_CONFIGURE=1 -j1 V=s 延伸阅读 Linux 安装 Docker Engine 与 Docker Compose Docker 使用 macvlan 部署 OpenWrt 旁路由 ","date":"2021-03-05T00:00:00Z","permalink":"/posts/docker-build-immortalwrt/","title":"Docker 编译 ImmortalWrt（Ubuntu 24.04 容器示例）"}]