MoreRSS

site iconSanguok | 山月修改

※興趣 ①閩語 ②文言 ③漢字 ④東亞各種文獻 ⑤劇畫稗玩/ACGN ⑥平面設計與排印
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Sanguok | 山月的 RSS 预览

在Windows上為GnuCash啟用線上報價 (Finance::Quote)

2025-10-29 19:26:51

GnuCash內置了「線上報價」(Finance::Quote) 功能,允許軟體自動從網路獲取股票、基金和貨幣的最新價格。

但在Windows系統上,安裝此功能時常因缺少依賴項或權限設定而導致失敗。本教程將說明完整的安裝步驟,以及如何修復常見的依賴安裝錯誤,以確保功能正常運作。

先決條件

  1. 已經在Windows 上安裝了GnuCash(安裝包內含Strawberry Perl環境)。
  2. 擁有電腦的管理員權限

第1步:以管理員身份開啟PowerShell

安裝過程需要使用PowerShell操作。

  1. 點擊「開始」選單。
  2. 輸入PowerShell
  3. 在搜尋結果中,找到 “Windows PowerShell”。
  4. 右鍵點擊它,選擇「以管理員身份執行」。
  5. 開啟後應會看到標示為管理員的PowerShell視窗。

第2步:更改PowerShell執行策略

Windows預設可能會限制腳本執行。需要先為當前的PowerShell視窗解除限制。

  • 在藍色窗口中,複製並貼上以下指令,然後按Enter鍵:

    Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
    
  • 如出現提示,請輸入Y並按Enter鍵確認。

第3步:切換至GnuCash目錄並執行安裝

接下來執行GnuCash提供的安裝腳本。

  1. 切換到GnuCash的程式目錄。複製並貼上以下指令,然後按Enter(一般來說是此路徑,但還是要確認下自己電腦內實際的安裝路徑):

    cd "c:\Program Files (x86)\gnucash\bin"
    
  2. 執行.ps1安裝腳本(注意是.ps1文件,不是.cmd):

    .\install-fq-mods.ps1
    

第4步:處理依賴項安裝失敗的問題

執行上述腳本時,日誌中可能會出現Result: FAILNOT OK的錯誤訊息。這是因為Finance::Quote依賴許多Perl模組,若其中任何一個測試失敗,安裝過程就會受到影響。

即使安裝腳本最後顯示:

>> Installation succeeded <<

Press Enter to continue...

若前方的日誌中有報錯,代表並未真正安裝成功。必須手動修復這些失敗的依賴項。

例1:修復Date::Simple失敗

如果日誌顯示類似以下的錯誤:

Result: FAIL Failed 1/3 test programs. 1/233 subtests failed. gmake: *** [makefile:1034: test_dynamic] Error 1 IZUT/Date-Simple-3.03.tar.gz C:\STRAWB~1\c\bin\gmake.exe test -- NOT OK Stopping: 'install' failed for 'Date::Simple'.

這表示Date::Simple模組因測試未通過而中止安裝。

修復方法:

  1. 同一個管理員PowerShell視窗中,輸入以下指令啟動Perl的CPAN安裝環境(一般來說是此路徑,但還是要確認下自己電腦內實際的安裝路徑):
    c:\Strawberry\perl\bin\cpan.bat
    
  2. 提示符會變為cpan[1]>
  3. 輸入force install指令強制安裝該模組(跳過測試):
    force install Date::Simple
    
  4. 等待安裝完成。
  5. 輸入exit並按Enter鍵,退回到PowerShell提示符。

例2:修復Module::CPANTS::Analyse失敗

修復完前一個模組後,必須重新執行安裝腳本:

.\install-fq-mods.ps1

此時可能會發現另一個模組失敗。例如:

Result: FAIL [中略] Stopping: 'install' failed for 'I/IS/ISHIGAKI/Module-CPANTS-Analyse-1.02.tar.gz'.

修復方法:

  1. 再次啟動CPAN(一般來說是此路徑,但還是要確認下自己電腦內實際的安裝路徑):
    c:\Strawberry\perl\bin\cpan.bat
    
  2. 強制安裝這個失敗的模組:
    force install Module::CPANTS::Analyse
    
  3. 安裝完成後,輸入exit退出。

重複此過程

重複上述步驟,即:

  1. 執行.\install-fq-mods.ps1
  2. 檢查日誌是否有... NOT OK的錯誤。
  3. 若有,進入cpan.bat使用force install [模組名]進行修復。
  4. 直到最後一次執行安裝腳本(.\install-fq-mods.ps1)時,日誌顯示所有測試均為PASSOK,並顯示:

BPSCHUCK/Finance-Quote-1.67.tar.gz C:\STRAWB~1\c\bin\gmake.exe install UNINST=1 -- OK >> Installation succeeded <<

此時才代表安裝真正成功。

第5步:驗證安裝

安裝完成後,需驗證GnuCash能否正確載入模組。

  1. 關閉目前的管理員PowerShell視窗。
  2. 以一般使用者身份開啟一個的「命令提示字元」 (CMD) 視窗。
  3. 輸入以下指令進行測試(一般來說是此路徑,但還是要確認下自己電腦內實際的安裝路徑):
    "c:\Program Files (x86)\gnucash\bin\gnucash-cli.exe" --quotes info
    
  4. 判斷結果:
    • 如果出現Failed to initialize... missing_modules,表示安裝仍未成功,需回頭檢查依賴項。
    • 如果出現一條警告(WARN),如下所示,則代表安裝成功
    * 20:09:31  WARN <gnc.price-quotes> [GncFQQuoteSource::set_api_key()] No Alpha Vantage API key set...
    

Alpha Vantage API key警告屬於正常現象,僅提示未設定特定數據源的API金鑰,但證明GnuCash已成功載入Finance::Quote模組。

總結

  1. 開啟GnuCash。
  2. 進入工具 (Tools) -> 證券編輯器 (Security Editor)
  3. 選擇一個證券(如股票),點擊「編輯」。
  4. 勾選「獲取在線報價(G)」。
  5. 此時原本顯示「Finance::Quote 未正確安裝」的警告訊息應已消失。

注意,Finance::Quote適用於獲取股票和主要貨幣匯率,但通常無法自動獲取——比方說中國的場外基金淨值等。對於此類基金,大概還是得定期前往 工具 -> 價格編輯器 (Price Editor) 手動更新淨值。

Gitea Docker /var/empty 权限问题除错总结

2025-07-19 16:47:24

维护Gitea的Docker容器。见到日志中持续出现关于/var/empty目录的权限错误。据错误讯息,该目录的所有者应为root用户,且不应授予其他用户以写入权限。

此问题的根源,在于Gitea容器内部。由于/var/empty目录是容器中SSH服务所需,其运行环境位于容器内,因此需要在容器中进行修正,而非在宿主机上操作。

解决步骤如下:

  1. 进入Gitea容器的命令行界面;
  2. 执行chown root:root /var/empty,将该目录的所有者变更为root
  3. 执行chmod 755 /var/empty,设定正确的目录权限。

完成上述两个命令后,问题即得解决。日志显示SSH服务已在22埠(端口)上成功启动并开始监听,Gitea恢复正常运作。

Bookwyrm由0.7.5升级至Production(e217a17)完整过程及疑难解答

2025-07-06 15:28:54

引言

本教程旨在帮助Bookwyrm用户如何将其部署从v0.7.5升级到(笔者执笔时)最新的production分支(具体到e217a17版本)。截至该版本提交(commit)的诸多代码带来了许多改进,例如对搜索词汇权重的优化设计(能更精准地根据标题、作者、系列等信息进行排序),以及数据导出功能的增强(排除已删除内容)等,这些都会提升用户体验和系统稳定性。

在升级过程中,可能会遇到一些并非普遍存在,但对于特定部署环境至关重要的特殊情况。如果您符合以下条件——

  1. 使用外部(包括宿主机上的)数据库服务,并依赖Docker的host网络模式进行通信;同时,
  2. 通过容器外的服务进行HTTPS管理(而非在容器内部自签)

——那么本教程中的“特殊情况”部分将提供相应的解决方案。

如果您没有这些特殊需求,通常可以直接遵循Bookwyrm官方的升级指南,那将是更简便的路径。

特殊情况概述

本次升级案例中的主要挑战源于以下几个相互关联的部署配置:

  1. 外部数据库与host网络模式: 为了与宿主机上运行的外部PostgreSQL数据库服务进行通信,笔者的Docker Compose配置中将db服务注释掉了,并将webcelery等服务设置为 network_mode: "host"

    • 影响:host网络模式下,Docker容器直接共享宿主机的网络堆栈,容器之间无法通过服务名称进行内部解析。它们必须通过宿主机的IP地址(通常宿主机实际的局域网IP;或者都是host了,干脆127.0.0.1亦可)进行通信。这直接影响了nginx与Web服务之间的通信配置。
  2. Cloudflare Zero Trust与HTTPS管理: 笔者用 Cloudflare Zero Trust来处理HTTPS加密。这意味着服务器端无需强制进行HTTPS,而是由Cloudflare在边缘网络提供SSL/TLS保护。

    • 影响: 如果在服务器端启用自签名或其他免费的HTTPS证书,并与Cloudflare的“严格”HTTPS策略冲突,可能导致Cloudflare阻止流量,认为存在不安全的端到端连接。因此,我们的nginx配置必须避免在服务器端强制HTTPS,而是通过HTTP监听,并由Cloudflare Zero Trust负责转发和加密。这与Bookwyrm官方版本(无论是截至v0.7.5抑是截至本文所提之“最新”版)中可能集成的Certbot自动化HTTPS方案有所冲突,需要进行定制化。
  3. nginx端口冲突: 由于采用host网络模式,nginx容器会尝试直接监听宿主机的端口。在升级过程中,我们发现宿主机的80端口已被一个别的(就诸如nginx这样的)Web服务器占用,导致nginx无法启动。这需要沉下心诊断并解决端口冲突问题。

这些特殊因素叠加,使得简单的拉取(pull)或者复制粘贴新版本docker-compose.yml配置变得不可行,需要对网络、nginx代理和端口管理进行精细调整。

升级教程:从0.7.5到Production (e217a17)

本教程假设您已经有了一个运行正常的Bookwyrm 0.7.5部署,并且熟悉基本的Git和Docker Compose命令。

步骤1:备份数据和配置

关键:在进行任何重大升级之前,请务必备份所有重要数据。

  1. 备份数据库: 如果数据库运行在Docker容器内(假设容器名为bookwyrm-db-1),可以使用docker exec命令进行备份:

    docker exec -t bookwyrm-db-1 pg_dump -U your_db_user your_db_name > db_backup.sql
    

    如果数据库是外部服务,则使用其提供的备份机制。

  2. 备份 Docker Compose 文件和自定义配置: 复制当前的docker-compose.yml文件和所有自定义的配置文件(例如.env文件、nginx配置目录 nginx/ 等)。

    cp docker-compose.yml docker-compose.yml.bak
    cp .env .env.bak
    cp -r nginx/ nginx.bak/
    # 确保备份所有可能包含定制化信息的目录和文件
    

步骤2:更新Bookwyrm代码库

  1. 停止当前运行的Bookwyrm服务:

    docker compose down
    
  2. 拉取最新的production分支代码并回滚:

    (请确保您明白本节在说什么之后,再继续操作!如于不熟悉Git之情况下贸然照搬,会导致可怕后果😱)

    由于过去站点的运行可能会生成很多不需要的变更甚至提交(例如,nginx静态文件目录的权限变更,或者是Windows/Linux之间的换行符差异,皆会被Git一一记录);故此,先回滚到干净的远程production状态,然后只应用您所需的修改。

    git fetch origin                   # 获取远程仓库的最新状态
    git reset --hard origin/production # 强制重置本地分支到远程 production 的最新提交
    

    这将清除您本地所有未推送到origin/production的修改。(或者可能是upstream/production,根据实际情况来。)

步骤3:应用有价值的修改

(本节同样也是,希望您明白下述文字在说什么以后,再考虑要否采纳。)

现在,在干净的origin/production基础上,重新应用希望保留修改:

  1. 重命名docker-compose.ymldocker-compose.example.yml 如果origin/production包含docker-compose.yml文件,执行:

    mv docker-compose.yml docker-compose.example.yml
    

    兹解释一下笔者为什么会需要这么做。笔者以为官方Git库直接提供docker-compose.yml并于每回释出新版本时累次将变更付诸此文件是不优雅的。这样做没有考虑到(包括笔者在内)魔改docker-compose.yml以取消掉部分原生服务(例如容器内的db)的可能性——每次拉取官方更新,若不备份好自己的docker-compose.yml,就会被官方发布的.yml一整个覆盖掉。

    笔者以为,较为优雅的实践,是官方提供一个docker-compose.example.yml,并指导用户去:

    cp docker-compose.example.yml docker-compose.yml
    

    岂不美哉?不过,反馈给官方后,项目的积极维护者所分享的考量也不无道理——

    对于像BookWyrm这样(笔者瞎按:由于处于内测阶段所以会急遽更迭)的项目来说,使用标准并有助于保持一致的环境是非常典型的。一般来说,自定义应该是环境变量。

    (笔者中译)

    官方倒也有提供docker-compose.override.yml这样的维护方式。记入在docker-compose.override.yml的配置如与docker-compose.yml冲突,优先执行的是override的。且override.yml也在.gitignore内,就不会在每次更新时被覆盖了。但是,笔者这种需要注释掉部分服务的做法,override支持不到了

    所以,截至本文执笔时,还是只能自行重命名。可以考虑fork一份Git库,然后把重命名操作应用过去。在这种情况下,我们就可以将upstream指定为官方Git库,origin指定为自己的Git库。

  2. .gitignore中增加临时文件的忽略规则: 打开本地的.gitignore文件,并在文件末尾或其他合适位置添加增加临时文件的忽略规则(笔者例)。如果您像笔者一样,宿主机用的是魔改版的Linux系统,而该系统恰会以自己的方式为一些图片文件生成临时缩略图的话,就需要忽略之。否则,nginx静态文件目录(./static)下面会出现很多临时缩略图待提交——或者在哪次直接不慎提交掉了。

  3. 提交这些更改:

    git add docker-compose.example.yml .gitignore # 根据实际重命名情况调整
    git commit -m "Rename docker-compose.yml to docker-compose.example.yml and add temp files ignore to .gitignore"
    

步骤4:调整docker-compose.yml和nginx配置以适应特殊情况

这是本次升级教程的核心和关键,将根据特殊需求定制docker-compose.yml和nginx配置。

  1. 手动更新 docker-compose.yml 基于docker-compose.example.ymlproduction提供的docker-compose.yml文件,进行以下修改:

    • nginx, web, celery_worker, celery_beat等实际有启用的服务设置 network_mode: "host"

      services:
        nginx:
          # ...
          network_mode: "host"
          # ...
        web:
          # ...
          network_mode: "host"
          # ...
        celery_worker:
          # ...
          network_mode: "host"
          # ...
        celery_beat:
          # ...
          network_mode: "host"
          # ...
      
    • nginx服务volumes调整 (核心): 笔者将放弃官方的default.conf.template模板机制,而是直接挂载一个包含所有自定义nginx配置的文件,并让它直接覆盖容器内nginx的默认配置。

      首先,在本地./nginx/目录中,准备一个名为my-bookwyrm-nginx.conf的文件(或者选择用https.conf也可以),将所有自定的nginx配置内容都放入其中。

      nginx服务volumes部分应类似于:

          volumes:
            # 直接将你的主Nginx配置文件挂载到容器的 /etc/nginx/conf.d/default.conf
            # 确保这个文件包含了所有你需要的Nginx指令,并且是最终版本
            - ./nginx/my-bookwyrm-nginx.conf:/etc/nginx/conf.d/default.conf
      
            # 如果你的 server_config, server_name, locations 是独立的Nginx include文件
            # 并且你希望它们继续被加载,你仍然可以挂载它们。但要确保它们不包含监听指令,
            # 而是被 my-bookwyrm-nginx.conf 文件通过 `include` 指令引用。
            # 示例:
            # - ./nginx/locations:/etc/nginx/conf.d/locations
            # - ./nginx/server_config:/etc/nginx/conf.d/server_config
            # - ./nginx/server_name:/etc/nginx/conf.d/server_name
      
            # 99-autoreload.sh 脚本的挂载
            - ./nginx/99-autoreload.sh:/docker-entrypoint.d/99-autoreload.sh
      
            # 静态文件和媒体文件卷 (改为直接挂载本地目录)
            - ./static:/app/static
            - ./images:/app/images
            - ./exports:/app/exports # 用于导出数据
      

      注意:docker-compose.yml中,如果Nginx服务仍然有ports部分,请确保将其移除或注释掉。network_mode: "host"会让Nginx直接使用宿主机网络,ports映射是多余的。

    • 按需添加redis_activityredis_brokerflower(用于监控Celery)服务。(笔者用了外部的服务,就没开这些个。)

    • web服务的depends_on调整:

      web:
        # ...
        depends_on:
          celery_worker:
            condition: service_started
          redis_activity:
            condition: service_started
          # 以及其他你实际有开启的
      
    • 定义具名卷 (volumes) 在docker-compose.yml底部: 所有在服务中使用的具名卷(例如 redis_activity_dataredis_broker_datapgdatabackups)都需要在这里定义。

      volumes:
        pgdata:
        backups:
        static_volume: # 如果你用具名卷而不是直接挂载目录
        media_volume: # 如果你用具名卷而不是直接挂载目录
        exports_volume:
        redis_broker_data:
        redis_activity_data:
      

      重要: 如果你的webcelerynginx已经使用network_mode: "host"并直接挂载本地目录static, images, exports,那么static_volume, media_volume等具名卷就不需要了。保持一致性,如果挂载本地目录,就不要定义具名卷,反之亦然。

  2. 创建或更新nginx主配置文件 (./nginx/my-bookwyrm-nginx.conf): 这个文件将是nginx容器实际加载的/etc/nginx/conf.d/default.conf

    • 确保upstream web指向127.0.0.1:8000

      upstream web {
          server 127.0.0.1:8000; # 因为web容器在宿主机上也监听这个地址
      }
      
    • 配置nginx监听的端口 (关键步骤): 由于Apache占用80端口,且打算通过Cloudflare Zero Trust处理HTTPS,Nginx应该监听一个未被占用的端口(例如8001),并只提供HTTP服务。

      # 定义一个 Nginx 缓存区域 (可选,但推荐)
      proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=bookwyrm_cache:10m inactive=60m;
      
      server {
          listen [::]:8001; # Nginx 监听宿主机的 8001 端口
          listen 8001;
      
          server_name sanguok.com; # 示例。换成你的域名。下同
      
          # 最大请求体大小,根据需要调整
          client_max_body_size 10M;
      
          # 确保只通过主域名访问
          if ($host != "sanguok.com") {
              return 301 $scheme://sanguok.com$request_uri;
          }
      
          # 主要的反向代理到 web 服务
          location / {
              proxy_pass http://web; # upstream web 已经指向了 127.0.0.1:8000
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header Host $host;
              proxy_redirect off;
      
              # 缓存设置 (与上面 proxy_cache_path 配合使用)
              proxy_cache bookwyrm_cache;
              proxy_cache_valid any 1m;
              add_header X-Cache-Status $upstream_cache_status;
              proxy_ignore_headers Cache-Control Set-Cookie Expires;
              proxy_cache_methods GET HEAD;
              proxy_no_cache $cookie_sessionid;
              proxy_cache_bypass $cookie_sessionid;
          }
      
          # 专门处理登录、密码重置等,通常有限制请求
          location ~ ^/(login[^-/]|password-reset|resend-link|2fa-check) {
              # limit_req zone=loginlimit; # 如果定义了限速区域
              proxy_pass http://web;
          }
      
          # 静态文件服务
          location /static/ {
              root /app; # 对应 docker-compose.yml 中的 ./static:/app/static
              try_files $uri =404;
              add_header X-Cache-Status STATIC;
              access_log off;
          }
      
          # 图像文件服务
          location /images/ {
              location ~ \.(bmp|ico|jpg|jpeg|png|svg|tif|tiff|webp)$ {
                  root /app; # 对应 docker-compose.yml 中的 ./images:/app/images
                  try_files $uri =404;
                  add_header X-Cache-Status STATIC;
                  access_log off;
              }
              return 403; # 阻止非图片文件访问
          }
      
          # favicon.ico 处理
          location /favicon.ico {
              root /app/images/logos; # 假设你的 favicon 在这个路径
              try_files /IMG_5682.ico =404; # 确保指向实际的图标文件
          }
      
          # Flower 监控界面 (如果启用了 flower 服务)
          location /flower/ {
              proxy_pass http://127.0.0.1:8888; # 如果flower也在host模式下监听,并且监听8888端口
              proxy_cache_bypass 1;
          }
      
          # 其他常用的 Nginx 优化设置
          sendfile on;
          tcp_nopush on;
          tcp_nodelay on;
          keepalive_timeout 65;
          types_hash_max_size 2048;
          gzip on;
          gzip_disable "msie6";
          proxy_read_timeout 1800s;
          chunked_transfer_encoding on;
      
          # 错误日志
          error_log /var/log/nginx/error.log debug;
          access_log /var/log/nginx/access.log; # 可以改为 cache_log
      }
      
      # 如果有其他 Nginx 配置 include,确保其内容正确且不会监听 80 端口
      # 例如:
      # include /etc/nginx/conf.d/server_config;
      # include /etc/nginx/conf.d/server_name;
      # include /etc/nginx/conf.d/locations;
      
    • 关于99-autoreload.sh的权限: 再次确认nginx/99-autoreload.sh文件具有执行权限 (chmod +x ./nginx/99-autoreload.sh)。

步骤5:更新.env文件

Bookwyrm和Redis服务会引入新的环境变量。打开.env文件并更新:

  • 数据库连接信息: 确保与你的外部数据库服务匹配。
  • Redis密码和端口:redis_activityredis_broker设置强密码和端口(与你在docker-compose.yml中定义的Redis命令相符)。
    # Redis for Celery Broker
    REDIS_BROKER_PASSWORD=your_broker_redis_password
    REDIS_BROKER_PORT=6379 # 或者你指定的端口
    
    # Redis for Activity Stream/Cache
    REDIS_ACTIVITY_PASSWORD=your_activity_redis_password
    REDIS_ACTIVITY_PORT=6380 # 或者你指定的端口
    
    # Flower (Celery 监控) 用户名和密码
    FLOWER_USER=your_flower_username
    FLOWER_PASSWORD=your_flower_password
    
    # Nginx 相关的环境变量 (如果你的Nginx配置依赖它们)
    DOMAIN=sanguok.com
    # NGINX_SETUP=https # 如果你打算用我的my-bookwyrm-nginx.conf,这一行可以不设置或删除
    
    根据实际的docker-compose.ymlREDIS_BROKER_PORTREDIS_ACTIVITY_PORT的配置来填写端口。

步骤6:构建和启动服务

  1. 构建Docker镜像:

    docker compose build
    

    这会根据更新的Dockerfiledocker-compose.yml来构建所有服务的镜像。

  2. 启动所有服务:

    docker compose up -d
    

    这将以后台模式启动所有容器。

  3. 检查日志以确认服务是否正常运行:

    docker compose logs -f
    

    密切关注nginx、Web和Celery服务的日志,确保它们没有错误并且正常监听。

步骤7:执行数据库迁移和初始化

首次启动新版本时,可能需要执行数据库迁移。

  1. 执行数据库迁移:

    docker compose exec web python manage.py migrate
    
  2. 创建超级用户 (如果还没有):

    docker compose exec web python manage.py createsuperuser
    
  3. 收集静态文件:

    docker compose exec web python manage.py collectstatic --noinput
    

步骤8:配置nginx和Cloudflare Zero Trust

  1. 确保宿主机上没有其他服务占用nginx计划监听的端口。 检查你的宿主机,确保其他服务没有占用8001端口 (如果你按照本教程将其配置为nginx的监听端口)。

    sudo lsof -i :8001 # 检查 8001 端口是否被占用
    

    如果被占用,需要停止占用8001端口的服务,或者将nginx配置为监听另一个空闲端口。

  2. Cloudflare Zero Trust配置: 在Cloudflare Zero Trust控制台,为域名(按前面的示例就是sanguok.com)配置应用程序,使其通过HTTP转发到你的服务器IP地址的8001端口。Cloudflare将负责从客户端到Cloudflare的HTTPS连接。

恭喜!

经过这些详细的步骤和针对特殊情况的调整,您的Bookwyrm实例应该能够成功从0.7.5升级到production分支的e217a17版本,并且在特定的Docker Host网络模式和Cloudflare Zero Trust环境下正常运行。

如果在升级过程中遇到问题,请检查服务日志,并对照本教程中的步骤进行排查。祝君顺利!

Bandizip編輯檔案後未提示更新?莫慌!幫您秒速找回臨時存檔

2025-06-27 12:44:56

您是否曾經歷過這樣的驚魂時刻——

透過Bandizip直接打開壓縮檔裡的Word或Excel文件,埋頭苦幹了半天,按下儲存並關閉檔案後,卻沒有像往常一樣跳出「是否要將變更更新至壓縮檔?」的提示。Bandizip靜悄悄的,彷彿什麼事也沒發生。這時候,一陣冷汗冒上背脊——難道剛剛編輯的內容全都消失了嗎?

——先別慌張!您辛苦的成果,極有可能還安全地躺在電腦的某個角落。這篇文章,將帶您了解背後的原理;並提供清晰的步驟:助君迅速找回那個「失蹤」的臨時檔案。

何以如此?
解密Bandizip的「幕後工作」

首先,我們需要明白,任何壓縮軟體(包括Bandizip)都無法直接修改壓縮檔內的檔案。當您對著壓縮檔裡的文件點擊兩下時,實際發生了以下事情:

  1. 解壓縮副本:Bandizip會在背景將該檔案的一個「副本」解壓縮到一個系統指定的「臨時資料夾 (Temporary Folder)」中。
  2. 編輯副本:您所使用的編輯軟體(如Microsoft Office)其實是打開了這個臨時副本。您所有的編輯、儲存動作,都是針對這個副本進行的。
  3. 偵測與更新:在理想情況下,當您關閉編輯軟體後,Bandizip會偵測到這個副本被修改了,於是跳出提示,詢問您是否要用這個修改過的副本去覆蓋壓縮檔裡的原檔案。

然而有時候,或因軟體的衝突,或權限的問題,或不明原因的程式小錯誤;最後這個「偵測與更新」的步驟失敗了,導致提示視窗沒有出現。

救援步驟
——找回臨時存檔

當您發現更新提示沒有出現時,請依照以下步驟操作。最關鍵的一點是:盡量不要關閉Bandizip,並立即行動!因為有些暫存檔可能會在程式關閉後被自動清理。

第一步
——打開「執行」對話框

同時按下鍵盤上的Windows標誌鍵⊞ + R鍵。這個組合鍵會快速叫出系統的「執行」視窗。

第二步
——輸入指令%temp%

在「執行」視窗的輸入框中,準確輸入以下內容(包含兩個百分比符號),然後按下「確定」或Enter鍵:

%temp%

這個指令是一個系統捷徑,會立刻打開目前使用者的臨時資料夾,省去您在複雜的C:\路徑中尋找的麻煩。

第三步
——在臨時資料夾中找尋您的檔案

臨時資料夾裡通常會存放大量的暫存檔,看起來可能有些混亂。別擔心,使用以下技巧可以快速定位目標:

  1. 排序檔案。在資料夾空白處點擊右鍵,選擇「排序方式」,然後選擇「修改日期」。這樣,最近被修改過的檔案就會排列在最頂端。
  2. 辨識檔案:
    • 剛剛編輯的檔案(例如我的報告.docx)很可能就直接出現在列表的最上方。
    • 有時候,Bandizip會建立一個專屬的暫存資料夾來存放解壓縮的檔案。這個資料夾的名稱通常以BNZ_bz開頭。如果您沒直接看到檔案,可以找找看有沒有類似名稱的資料夾,您的檔案就在裡面。

第四步
——立刻備份檔案!

一旦找到了您編輯過的檔案,請立刻將它複製(不要剪下)到一個安全的位置,例如桌面、您的文件資料夾等。這樣可以確保您的心血結晶被安全地保存下來。

最佳實踐
——避免未來再次發生

雖然上述方法是有效的救援手段,但最好的策略還是預防。如果您需要對壓縮檔內的檔案進行重要或長時間的編輯,建議採用更穩健的作法:

  1. 先手動將檔案從壓縮檔中解壓縮出來。
  2. 在您電腦的普通資料夾(如桌面)中打開並編輯這個解壓縮出來的檔案。
  3. 編輯完成並儲存後,再手動將這個修改過的檔案拉回Bandizip視窗中,以更新壓縮檔。

此方法雖然步驟稍多,但能確保檔案正確儲存,避免因暫存機制異常而導致資料遺失。

用正则表达式修改ruby标签

2025-06-09 20:32:11

在支持HTML的环境(包括许多Markdown编辑器1),可以使用<ruby>标签为汉字等表意文字添加注音(如拼音振假名)。不过,在不支持该标签的纯文本环境中,内容的可读性会因此降低。

例如,一个标准的<ruby>标签:

<ruby>大学<rt>だいがく</rt></ruby>

在纯文本的渲染环境中会显示为大学だいがく,这显然不够理想。2

兼容性方案
——使用<rp>标签

HTML标准提供了<rp>(ruby parenthesis)标签来处理这种兼容性问题。它用于包裹括号一类的“备用字符”,这些字符只在不支持<ruby>标签的环境中显示。

添加<rp>标签后的理想格式如下:

<ruby>大学<rp>(</rp><rt>だいがく</rt><rp>)</rp></ruby>

这样,可以两全其美:

  • 在支持的环境3,正常显示注音样式,括号不显示。
  • 在不支持的环境(纯文本),显示为大学(だいがく),未尝有损可读性。

那么,如何将手上的文字(譬如说电子笔记库)中所有旧格式的<ruby>标签批量升级到这种新格式呢?自然会想到的是全局搜索与正则表达式。

第一版方案
——简单替换

警告:请不要急着一行行跟着以下方案批量替换,建议先看看下文再做决定。另外,任何时候都请记得做好备份,再批量修改。)

这里用可以直接管理包括.markdown文件在内的笔记文档的VS Code来举例。我们最直接的想法是找到所有的注音标签,然后给它们加上括号。

  • 查找:<rt>(.*?)<\/rt>
  • 替换:<rp>(</rp><rt>$1</rt><rp>)</rp>

这个方案可以处理最简单的情况。但很快我们就会发现第一个问题。

边界情况
——一个词组内的多个注音

当遇到像日语“しん”这样的词,其源代码可能是:

<ruby>地<rt>じ</rt>震<rt>しん</rt></ruby>

上面的简单方案能正确工作吗?是的。它会独立地将<rt>じ</rt>替换为<rp>(</rp><rt>じ</rt><rp>)</rp>,并将<rt>しん</rt>替换为<rp>(</rp><rt>しん</rt><rp>)</rp>,最终得到正确的结果:

<ruby>地<rp>(</rp><rt>じ</rt><rp>)</rp>震<rp>(</rp><rt>しん</rt><rp>)</rp></ruby>

到目前为止,一切似乎都很顺利。但一个更隐蔽的“定时炸弹”埋藏在这个方案中。

第二版方案
——解决重复执行的bug

问题在于,如果我们不小心再次运行4这个替换脚本,会发生什么?

它会找到已经修复好的<rp>(</rp><rt>じ</rt><rp>)</rp>中的<rt>じ</rt>,然后再次为它套上括号,导致灾难性的结果:

<rp>(</rp><rp>(</rp><rt>じ</rt><rp>)</rp><rp>)</rp>

这破坏了幂等性5原则。一个健壮的脚本必须能够安全地重复执行而不产生副作用。

因此,查找规则需要改进,目标是:只查找那些尚未被<rp>(</rp><rp>)</rp>包裹的<rt>标签。

最终方案
——用反向否定查找

正则表达式的“反向否定查找”(negative lookbehind),写作(?<!...),是解决这个问题的完美工具。它允许我们匹配一个模式,但前提是它的前面是某个特定的字符串。

最终的查找与替换规则

  1. 打开VS Code的“在文件中查找和替换”面板 (Ctrl+Shift+HCmd+Shift+H)。
  2. 确保启用正则表达式模式 (点击 .* 图标)。
  3. 在“查找”(Find)输入框中,粘贴以下表达式:(?<!<rp>\(<\/rp>)<rt>(.*?)<\/rt>
  4. 在“替换”(Replace)输入框中,粘贴以下内容:<rp>(</rp><rt>$1</rt><rp>)</rp>

工作原理解析

  • 查找:(?<!<rp>\(<\/rp>)<rt>(.*?)<\/rt>
    • (?<!<rp>\(<\/rp>):这是关键。它是一个反向否定查找,意思是:“从当前位置往前看,前面的文本不能是 <rp>(</rp>”。注意,为了匹配字面上的括号 (,我们用 \( 对其进行了转义。
    • <rt>(.*?)<\/rt>:这部分和之前一样,用于匹配 <rt> 标签及其内容。

这样,查找表达式就能准确地识别目标:

  • 匹配<ruby>地<rt>じ</rt>...中的<rt>じ</rt>,因为它的前面是字符,不是<rp>(</rp>
  • 不能匹配<ruby>地<rp>(</rp><rt>じ</rt>...中的<rt>じ</rt>,因为它前面的文本正好是我们排除了的<rp>(</rp>

如此一来,无论对文件执行多少次这个替换操作,它都只会影响那些尚未被处理的旧标签,而对已经符合新格式的标签没有影响,保证了操作的安全性和幂等性6

尾注

  1. 尽管<ruby>标签并不是标准的Markdown语法——从“<”与“>”就能看出它的XML色彩,但是不少现代的Markdown编辑器——例如Obsidian之类——都会尽量支持HTML语法,<ruby>标签也就包含在内了。 ↩︎
  2. 偏偏市面上大把网页都是这个样子,吾辈任重道远呐。 ↩︎
  3. 大到现代浏览器,小到Obsidian之类支援HTML的笔记软件的预览界面。 ↩︎
  4. 甚至还有种情况,就是——虽然是头一回尝试批量执行,但是待执行的文档里面已经包含有写好了<rp><ruby>标签了。 ↩︎
  5. 幂等性(idempotence)指的是在一次或多次执行相同操作后,系统的状态保持不变,即无论执行多少次,结果都是相同的。 ↩︎
  6. 对“幂等性”概念的解释参见前文。 ↩︎

为WordPress Syndication Links插件添加新的站点与图标的实现方法

2025-05-07 23:00:00

在实际使用Syndication Links插件的过程中,如果经常发布到一些插件默认不支持的网站,就会遇到无法显示对应站点图标和名称的问题。本文记录了如何通过不直接修改插件源文件的方式,为该插件增加新的站点映射与图标,以避免更新时丢失修改。

插件的站点映射机制

Syndication Links插件在includes/class-syn-link-domain-icon-map.php中维护了一个私有静态数组$map,用于将域名映射到对应的图标名。例如:

private static $map = array(
    'twitter.com'  => 'twitter',
    'facebook.com' => 'facebook',
    // ...
);

当页面需要显示某个链接的图标时,插件会:

  1. 从URL提取域名
  2. 检查$map数组是否有该域名的映射
  3. 如果有,则按映射名去svgs目录中加载对应的SVG图标文件
  4. 同时从includes/simple-icons.phpsimpleicons_syn_get_names()函数获取该映射名对应的显示名称

如果$map中没有对应域名,插件会尝试根据域名的中间部分去找图标文件;如果仍找不到,就会显示一个默认的website图标。

要添加新的站点映射,需要改动三个地方

  1. 域名映射表$map中添加新域名及其图标名,例如:

    'sanguok.com'         => 'wordpress',
    'chatan.cc'         => 'iceshrimp',
    'library.chatan.cc'         => 'bookwyrm',
    
  2. 图标名称映射includes/simple-icons.php中的simpleicons_syn_get_names()函数中,添加新的图标名与显示名称,例如:

    'bookwyrm' => 'Bookwyrm',
    
  3. SVG图标文件 在插件的svgs目录下放置与图标名一致的.svg文件,例如bookwyrm.svg。 文件内容需要是有效的SVG代码,来源可以是自己绘制的,或从SVG Repo等网站下载并调整。

避免插件更新覆盖修改

如果直接编辑插件的源文件,在更新插件后修改会丢失。 解决办法是使用Code Snippets插件,在运行时通过钩子动态扩展映射和图标名。

示例:通过Code Snippets扩展映射

<?php
// 扩展域名映射
add_filter('syn_link_mapping', function($mapped, $url) {
    $custom_map = array(
        'sanguok.com'         => 'wordpress',
        'chatan.cc'         => 'iceshrimp',
        'library.chatan.cc'         => 'bookwyrm',
    );

    $host = str_replace('www.', '', parse_url($url, PHP_URL_HOST));
    if (isset($custom_map[$host])) {
        return $custom_map[$host];
    }
    return $mapped;
}, 10, 2);

// 扩展图标名称映射
add_filter('simpleicons_syn_get_names', function($icons) {
    $icons['bookwyrm'] = 'Bookwyrm';
    return $icons;
});

说明:

  • syn_link_mapping过滤器允许在插件完成映射后,动态修改映射结果
  • simpleicons_syn_get_names是一个我们假设添加的过滤器,如果插件没有,需要通过其他方式覆盖函数返回值(可能需要额外包装)

关于SVG文件

  • SVG文件必须放在插件svgs目录下,否则插件找不到。
  • 更新插件时,svgs目录里的文件也可能被覆盖或删除,建议在更新前备份。

实际操作步骤

  1. 准备好新的域名与图标名映射表
  2. 准备对应的SVG文件
  3. 将SVG文件放到插件的svgs目录中
  4. 在WordPress后台Code Snippets插件中创建一个新的Snippet,将上面的PHP代码粘贴进去
  5. 保存并启用Snippet
  6. 刷新前台页面验证是否生效

总结

  • 核心流程是域名 → 映射名 → SVG 图标 → 显示名称
  • 不直接改插件源码,使用钩子和Code Snippets是比较安全的做法
  • SVG文件仍然需要放在插件目录中,但要注意更新时备份
  • 如果插件未来提供更灵活的API(例如允许自定义SVG路径),则可以完全不改动插件目录