MoreRSS

site iconSanguok | 山月修改

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

Inoreader Feedly Follow Feedbin Local Reader

Sanguok | 山月的 RSS 预览

告别复制粘贴噩梦:轻松获取Zoom录播全部文字记录的终极技巧

2025-07-14 04:35:07

免责声明

本文提供的脚本与方法仅供个人学习与技术交流之用。在浏览器控制台执行任何外部代码都存在潜在风险。请在充分理解代码功能、确认来源可信的情况下操作。对于因使用本文信息而可能导致的任何直接或间接后果,本文作者概不负责。请谨慎操作。

问题背景:为何无法一次性复制全部文字记录?

你可能遇到过这样的情况:在查看一个长达数小时的Zoom会议录播时,希望将右侧的语音转文字记录完整复制下来进行整理。但当你使用Ctrl+A全选和Ctrl+C复制后,粘贴到文本编辑器中的内容,却仅仅是屏幕上当前可见的一小部分。无论如何尝试,似乎都无法选中并复制全文。

这个问题并非程序错误,而是一项被广泛应用的前端性能优化技术——“虚拟滚动”(Virtual Scrolling)——所导致的。

为了提升长列表(如Zoom文字记录、社交媒体信息流等)的加载和滚动性能,网页并不会一次性将所有数据都渲染成页面元素。它仅会渲染当前视口(你所看到的屏幕范围)内的内容,以及视口上下方附近一小部分的“缓冲区”。

当你向下滚动页面时,浏览器会在后台执行两个关键操作:

  1. 卸载/回收:将已经滚动到屏幕上方的、不再可见的元素从DOM(文档对象模型)中移除。
  2. 加载/渲染:将即将进入屏幕下方的新内容数据渲染成新的元素,并添加到DOM中。

这种机制极大地减轻了浏览器的渲染压力,保证了页面的流畅度。但其副作用也很明显:在任何一个时间点,页面中实际存在的元素都只是全体内容的一个子集。因此,我们无法通过常规的“全选-复制”操作来获取完整文本。

核心思路:手动滚动与后台监听相结合

既然无法一次性获取所有内容,我们可以转变思路。核心的解决方案是:我们负责手动滚动,让一段预设的脚本在后台负责“监听”和“记录”。

这个思路可以分解为以下几点:

  1. 接受手动操作:Zoom这类成熟产品可能存在反自动化机制(防止机器人抓取数据),编写脚本来模拟自动滚动不仅复杂,而且成功率不高。因此,我们接受最可靠的方式——用手慢慢拖动滚动条。
  2. 建立“临时仓库”:在滚动之前,我们先通过浏览器控制台运行一段JavaScript代码。这段代码会在内存中创建一个“仓库”(例如一个Map对象),用来存放所有出现过的文字记录。
  3. 监听内容变化:脚本的核心是一个“监听器”(MutationObserver)。我们会用它来监视文字记录所在的特定HTML容器。这个容器可以通过浏览器的开发者工具(F12)进行元素审查来定位。
  4. 滚动即“投喂”:当我们手动滚动时,新的文字段落被渲染出来,监听器会立刻捕捉到这些新增的元素,提取其中的文本和唯一标识(如行号),然后存入我们的“仓库”。Map结构可以根据唯一标识自动去重,确保每条记录只存一次。
  5. 最终汇总:滚动结束后,我们再执行一个简单的命令,让脚本将“仓库”中收集到的所有文字片段,按正确的顺序拼接起来,并输出完整结果。

通过这种方式,我们将自动化脚本的复杂任务(模拟滚动)替换为可靠的手动操作,而将繁琐的、重复性的任务(记录与拼接)交给了代码,从而实现目标。

终极解决方案:三步获取完整记录

以下是具体的操作步骤。

第一步:部署监听脚本

首先,我们需要在浏览器中启动我们的“速记员”脚本。

  1. 在Zoom录播页面,按下键盘上的F12键(或者在页面上右键单击,选择“检查”或“Inspect”),打开开发者工具
  2. 在开发者工具面板中,找到并切换到“控制台” (Console) 标签页。
  3. 将下方提供的完整代码复制后,粘贴到控制台中,然后按下Enter键执行。
(function() {
    // --- 免责声明 ---
    console.log("请注意:执行此脚本意味着您已阅读并同意相关风险。脚本仅用于技术学习,请谨慎操作。");

    // --- 1. 创建一个“仓库”,用于存放所有记录下来的文字 ---
    // 使用 Map 数据结构,它可以根据唯一的“行号”(index)自动去除重复内容,保证数据唯一性。
    window.collectedTranscripts = new Map();
    
    // --- 2. 找到我们要监视的目标区域 ---
    // 这是Zoom录播文字记录所在的虚拟滚动容器的CSS选择器。
    const targetNode = document.querySelector('.vue-recycle-scroller__item-wrapper');
    if (!targetNode) {
        console.error('❌ 错误:未能找到文字记录的目标容器。脚本无法启动。请确认页面结构是否已更改。');
        return;
    }

    // --- 3. 创建并配置“监听器”(MutationObserver) ---
    // 它会观察目标区域的子元素变化(即文字记录的增删)。
    const observer = new MutationObserver(mutationsList => {
        for (const mutation of mutationsList) {
            // 我们只关心新添加到DOM中的节点。
            if (mutation.type === 'childList') {
                mutation.addedNodes.forEach(node => {
                    // 确保处理的是元素节点,忽略文本节点等。
                    if (node.nodeType !== 1) return; 
                    
                    const transcriptItem = node.querySelector('.transcript-list-item');
                    if (transcriptItem) {
                        const index = transcriptItem.getAttribute('data-index'); // 获取唯一行号
                        const textDiv = transcriptItem.querySelector('.text'); // 获取文本内容
                        
                        // 如果这是一个有效的文字条目,就格式化并记录下来。
                        if (index && textDiv) {
                            const nameSpan = transcriptItem.querySelector('.user-name-span');
                            let line = '';
                            // 如果有发言人,则添加发言人姓名。
                            if (nameSpan) {
                                line += `\n${nameSpan.innerText.trim()}: `;
                            }
                            line += textDiv.innerText.trim();
                            
                            // 将格式化后的行存入我们的“仓库”。
                            window.collectedTranscripts.set(parseInt(index), line);
                        }
                    }
                });
            }
        }
    });

    // --- 4. 让监听器正式上岗,开始监视目标区域的子元素列表变化 ---
    observer.observe(targetNode, { childList: true });
    
    // --- 5. 提供一个“完成”指令,用于在滚动结束后获取结果 ---
    window.showTranscript = function() {
        console.log('📝 正在整理已收集的全部文字记录...');
        observer.disconnect(); // 停止监听,任务完成。

        if (window.collectedTranscripts.size === 0) {
            console.log('🤷‍ 尚未收集到任何文字记录。您是否已经手动滚动了列表?');
            return;
        }

        // 整理笔记:将Map条目转换为数组,并按行号从小到大排序。
        const sortedEntries = Array.from(window.collectedTranscripts.entries()).sort((a, b) => a[0] - b[0]);
        const fullTranscript = sortedEntries.map(entry => entry[1]).join('\n').trim();

        // 尝试自动将结果复制到剪贴板。
        navigator.clipboard.writeText(fullTranscript).then(() => {
            console.log('✅ 操作成功!完整文字记录已复制到您的剪贴板。');
        }).catch(err => {
            console.error('❌ 自动复制失败:', err);
            console.log('📋 请手动复制下方的完整文字记录。');
        });
        
        // 在控制台打印完整记录,方便手动复制。
        console.log('\n--- 📜 完整文字记录如下 ---');
        console.log(fullTranscript);
        console.log('--- 📜 文字记录结束 ---');
    };

    // --- 6. 在控制台给出清晰的操作指引 ---
    console.log('✅ 监听器已成功启动!');
    console.log('下一步:请将鼠标聚焦于文字记录区域,然后【从头到尾完整地、缓慢地手动滚动】一次。');
    console.log('滚动完成后,请回到控制台,输入 `showTranscript()` 并按 Enter 键,即可获取完整内容。');

})();

执行后,如果一切顺利,你会在控制台看到“监听器已成功启动!”的提示。

第二步:手动滚动,完成数据“投喂”

现在,请将注意力移回Zoom页面的文字记录区域。使用鼠标滚轮、触控板或直接拖动滚动条,将文字记录从最顶端,平稳且完整地滚动到最底端。确保所有内容都在屏幕上至少出现过一次。这个过程就是在为我们的后台脚本持续提供数据。

第三步:执行命令,收获成果

当你确认已经滚动至列表底部,所有文字记录都已加载完毕后,再次回到浏览器的开发者工具控制台。

在其中输入我们预设的“完成”指令,然后按下Enter键:

showTranscript()

执行该命令后,脚本会立即停止监听,并将之前收集到的所有文字片段按照正确的顺序拼接成一份完整的记录。它会首先尝试将这份完整记录自动复制到你的系统剪贴簿,并同时在控制台中打印全文,以备自动复制失败时你可以手动复制。

现在,你可以打开任何文本编辑器(如记事本、Word等),使用Ctrl+V粘贴,即可看到完整的会议文字记录。

结语

本方法通过将可靠的手动操作与代码的自动化记录能力相结合,稳定且有效地解决了虚拟滚动列表下的文本复制难题。它不仅适用于Zoom录播,其核心思路也可以应用于其他采用类似技术的网页。

再次声明本文旨在技术分享,请在理解代码作用和潜在风险的前提下使用。对于任何不当使用所造成的后果,责任由使用者自负。

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

2025-07-06 15:28:54

引言

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

然而,本次升级过程中,笔者遇到了一些特殊情况,这些情况并非普遍存在,但对于拥有类似部署环境的用户来说至关重要。如果您是——

使用外部(包括宿主机上的)数据库服务,并依赖Docker的host网络模式进行通信;同时,

通过容器外的服务进行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視窗中,以更新壓縮檔。

這個過程雖然多了一兩個步驟,但它最可靠,能完全避免因臨時檔案機制失靈而導致的資料遺失風險。

控制笔记本温度与风扇噪音——以ASUS GL553VD为例

2025-06-12 21:27:41

前言

这几年笔记本电脑性能越来越强,但玩游戏或者跑大软件的时候,发热和风扇吵的问题也越来越厉害了。本文以陪伴了笔者多年的老机——ASUS GL553VD笔记本(i7-7700HQ + GTX 1050/1060)为例,汇总了从安装环境XTU/ThrottleStop调控、GPU控制风扇曲线、自动启动到硬件维护的完整降温降噪方案,附带各项技术细节,帮助追求静音与性能平衡的朋友们。


一、环境准备与兼容性解决

1. Intel XTU安装故障排查

Intel XTU(Extreme Tuning Utility)是英特尔官方推出的一款Windows平台下的系统级调校软件,允许用户对CPU的频率、电压、功耗等参数进行实时监控与精细调节,以追求更高性能或更低温噪。

当然,在安装它的过程中,可能会遇到报错。Intel XTU倘安装失败,是会输出日志文件,并弹窗提示用户查阅的(这点比隔壁NVIDIA要好太多)。最常见的是:

  • 错误码:0x80070643
    • 关键的报错信息(可以在日志里头找到):e000: Error 0x80070643: Failed to install MSI package.
    • 错误代码0x80070643是Windows Installer通用的“致命错误”,通常意味着:
      • 系统有待重启的更新或操作未完成(日志里也有RebootPending = 1);或者是
      • 先前安装残留或锁定了某些文件/注册表;要不然则
      • 安装程序权限不足或某个依赖(.NET Framework、VC++运行时)损坏。
  • 解决思路
    1. 重启系统,清除挂起更新;
    2. 确保.NET Framework ≥ 4.7.2、VC++ 2015-2022运行库完整;
    3. 以管理员模式运行安装程序。(值得一试!)

在经过上述排查后,倘若还是无法正确安装,那么可能是所使用的电脑系统版本、CPU型号、世代等不支持使用中的Intel XTU安装包。以笔者的情况为例,从官网下载到的最新版的Intel XTU(笔者写作当时下载的是2025年夏释出的10.0.1.31版)就无法正常安装,会提示曰——不适应当前的platform(平台)。好在官网提供两种下载包:

下面是两个版本的英特尔® XTU,版本 7.14.2.45 支持未锁频的英特尔® 酷睿™处理器(第 14 代)和更早的处理器,而版本 10.0 支持未锁频的英特尔® Core™ Ultra处理器(系列 2)及更高版本。

英特尔® Extreme Tuning Utility (英特尔® XTU)下载页面

所以选择去下载支持早期处理器的最终版本就好了。笔者的实测,也是能够无报错地顺利安装的。

2. XTU与VBS冲突

Unable to start Intel(R) Extreme Tuning Utility because of a system incompatibility.

XTU is incompatible with Windows VBS (Virtualization-based security).

Your CPU/BIOS does not have Undervolt Protection support as a mitigation, so you should check the status of services that can enable VBS. Click here to read more about Windows features that can enable VBS.

Click here to check the status of Windows VBS.

Consult the XTU documentation for more information.

Disabling Windows services or features may affect the functionality of other applications.

弹窗提示文本
  • 如上所示,Windows VBS(虚拟化安全)与XTU不兼容,会直接在尝试双击打开XTU后弹窗提示“CPU/BIOS does not have Undervolt Protection support”(CPU/BIOS不支持Undervolt Protection)而无法启动。
  • 解决
    1. 关闭核心隔离 → 内存完整性(Memory Integrity)
    2. (或者)PowerShell执行:
      # 关闭 Hyper-V
      dism.exe /Online /Disable-Feature:Microsoft-Hyper-V

      # 关闭 Windows Hypervisor Platform(如果启用了的话)
      dism.exe /Online /Disable-Feature:VirtualMachinePlatform
      命令执行完成后再重启。
      或者在提升权限的命令行里执行:
      bcdedit /set hypervisorlaunchtype off
      然后重启。
    3. (可选)组策略:
      • Win+R→ 输入gpedit.msc打开组策略编辑器
      • 计算机配置 → 管理模板 → 系统 → Device Guard → 禁用 “Turn On Virtualization Based Security”。
      • 重启

关闭VBS/Hyper-V会降低一部分系统安全(如HVCI、Credential Guard等保护功能),请权衡风险。这里提示:若不想关闭VBS,其实也未必要用XTU,直接使用下文的ThrottleStopNVIDIA Inspector等工具替代。

笔者的情况,是即便尝试关闭了Windows安全下的“内存完整性”设置,也无法通过XTU做任何调整电压的设置,所以只能放弃。

笔者打开XTU主界面后,迎面而来的是:

  • Power & Current Limits Optimized(一键优化电源和电流限制)与
  • Run Benchmark(内置基准测试)

俩按钮。Power & Current Limits Optimized系官方推荐之快速模式。按道理第一次调试前得先跑一次基准(Run Benchmark)并保存得分;再启用这个一键优化模式再跑一次:观察它给到的“预设优化”能带来多少收益。

这是一张XTU主界面的截图。反映了在笔者电脑上,Power & Current Limits Optimized按钮是灰色的,不能点。点击旁边的信息提示按钮ⓘ,指出:如果想超频,需要在Advanced View里对核心倍频和电压做小幅度调整。(但由于笔者的i7-7700HQ并不是K系列,倍频是锁死的,无法像台式K系列那样真正超频。所以按钮会灰掉。)在主界面没事儿可做,遂随便跑了下Run Benchmark,得分1127。
但是,XTU主界面提示倍频是锁死的

但是在笔者电脑上,Power & Current Limits Optimized按钮是灰色的,不能点。点击旁边的信息提示按钮,指出:如果想超频,需要在Advanced View里对核心倍频和电压做小幅度调整。但由于笔者的i7-7700HQ并不是K系列,倍频是锁死的,无法像台式K系列那样真正超频。所以按钮会灰掉。

在主界面没事儿可做,遂随便跑了下Run Benchmark,得分1127

左下角还有个Advanced View按钮,能展开更多的配置选单。点击后默认是System Infomation标签页,展示系统信息的。在左侧蓝色栏里,点击第二项Advanced Tuning(高级调校),尝试切过去——结果该标签页下一片空白。按道理在这里能做以Processor Core Voltage Offset(核心电压偏移)为首的Processor Core(处理器核心)设置,但是笔者这边就是没有。这一般不是软件错乱,而是笔者这台机子/BIOS微码把所有MSR可调寄存器都锁死了——XTU本身没东西可以调,自然就返回一片空白。


二、ThrottleStop深度调控

ThrottleStop是Windows下最强大的Intel CPU调控利器,能针对功耗、频率、电压进行细致调整。笔者此次安装了ThrottleStop 9.7版本。

主界面

1. 基本界面与配置档

主界面左上四个可选按钮代表4个profile,可以挑一个觉得名字合适的profile(或者把其中一个profile直接通过设置页改名),以进行后续的操作。

切档流程:选中按钮 → 修改参数 → SaveTurn On

下面开始介绍具体的配置方法。这里先且总结一下思路:

  1. EPP+低PL1/PL2+短冲刺时长 → 大幅降低平均功耗与温度,噪音跟着掉。
  2. 保留SpeedStep+C1E可确保空闲时更低功耗。
  3. 如果温度/噪音还高,可再小幅下调PL1(例如35W),或把EPP调到255

每次改完记得跑个短压测(如TS Bench、Prime95、3DMark)看温度/频率,找到「性能‐噪音‐温度」的最佳平衡点。

2. 主界面“Performance”区设置

  1. SpeedShift EPP:勾选它,然后把右边128的数值调高到200240(越大越偏向节能、低温)。
    • 注意,数字的修改,这边没有什么拖动条,而是直接将光标移动到数字上面,单击,然后编辑文本。
  2. SpeedStep & C1E:都勾上,让空闲时进深度节电态。
  3. BD PROCHOT:勾选,可以让CPU感知到VRM/芯片组的过热并配合降频(更安全);如果愿意稍微冒点风险,也可以先不勾。
  4. Disable Turbo要勾,这样还能保留短暂冲刺;真正靠下面的功耗限制来控温。

勾完以后,点左下的Save

3. FIVR:轻度降压(Undervolt)

  1. 点击FIVR → 勾选 Core & Cache 的 “Unlocked” → Offset Voltage 设为 –80 mV ~ –120 mV
  2. ApplySave

效果:核心与缓存双降压,稳态降温 5–10 ℃。

4. TPL:功耗极限控制

TPL设置界面
  1. 点击TPL打开 “Turbo Power Limits”。
  2. 取消Disable Controls,勾选并设置:
    • Long Power PL1 = 45 W + Clamp
    • Short Power PL2 = 60 W + Clamp
    • Turbo Time Limit = 10–15 s
  3. ApplyOK → (主界面)Save

效果:短时冲刺后迅速回到45 W,显著降低平均功耗与温度。

5. C-States & SpeedShift

  • 勾选C1E / C10:空闲时迅速进入深度睡眠态。
  • 勾选SpeedStep,保留涡轮但优先调度低频率。
  • Clock Mod(80–90%)或Set Multiplier:进一步锁频,必要时可用。

5. 启动与自动加载

ThrottleStop 9.7取消内置开机启动,推荐:

方法一:启动文件夹

Win+R → shell:startup
在此文件夹新建ThrottleStop.exe快捷方式,并在Options勾选Start Minimized

方法二:任务计划

  • 触发器:登录时;
  • 操作:启动ThrottleStop.exe,参数-minimized
  • 属性:以最高权限运行。

三、NVIDIA GPU节能调控

使用NVIDIA Inspector这款软件。

  1. Profile选_GLOBAL_DRIVER_PROFILE
  2. Sync and Refresh → Frame Rate Limiter,设60–75 FPS
  3. PowerMizer → Preferred Mode → Adaptive(自适应)或Optimal Power
  4. Overclock(若可):GPU Clock Offset、Mem Clock Offset设置为负值。
  5. Apply changes

四、风扇控制:Fan Control软件

Fan Control V226软件界面

使用开源的Fan Control(笔者用的是FanControl V226)替代BIOS风扇曲线(既然BIOS改不了的话),具体配置:

参数 建议值 说明
关闭温度 45 ℃ 非负载时保持静音
最小速度 10 % 风扇最低档
步进 (Step) 1 %/s 平滑升降
死区 (Dead) 2 ℃ 避免频繁抖动
负载温度 75 ℃ 达到此温度开始冲档
最大速度 80 % 保证足够散热,同时控制噪音
响应时间 1 s 温度变化后 1 秒再调速

高级方案:再建一条高负载曲线(负载温度85℃ → 最大100%),切换profile实现静音与散热兼顾。


五、BIOS / Armoury Crate 风扇曲线

笔者的情况下,BIOS无法修改风扇曲线。如果要尝试修改的话,可采取以下思路:

  • 进BIOS → Q-Fan Control → 选择Silent或自定义:
    • 低档起转:50℃ → 最高60–70%;
    • 确保重负载时仍保有散热余量。

六、系统电源计划微调

  • 处理器最大状态设为99%,禁止无限制Turbo;
  • 系统散热策略设为被动
  • 选用平衡省电方案,进一步降低功耗。

七、硬件维护与辅助散热

  1. 清灰重涂:清洁散热鳍片、风扇,重贴高端硅脂,降温3–5℃;
  2. 散热底座:选购带大风扇的底座,底部增风。
  3. 保持进风口与排风口通畅,避免堵塞。

结语

通过软件+固件+硬件三管齐下的方式,从CPU功耗、电压限制,到GPU节能配置,再到风扇曲线系统策略,你可以将笔记本的温度与噪音控制在一个令人满意的水平,同时不损失过多性能。每台机器与环境不同,建议分档测试压测(TS Bench、3DMark、FurMark、游戏),逐步微调,找到最适合你的平衡点。

祝你的笔记本静音顺畅,性能在线!

如何优雅地升级ruby标签:正则表达式指南

2025-06-09 20:32:11

在支持HTML的环境(包括不少现代的Markdown编辑器1)使用<ruby>标签为汉字等表意文字添加注音(如拼音振假名),是一个非常实用的功能。然而,一个常见的痛点是,在不支持该标签的纯文本渲染环境下,内容的可读性会大大降低。

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

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

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

优雅降级的方案:<rp>标签

HTML标准为此提供了一个优雅的解决方案:<rp>(ruby parenthesis)标签。它的作用是包裹“备用字符”(通常是括号),这些字符只在不支持<ruby>的环境中显示。

理想的格式应该是这样:

<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. 对“幂等性”概念的解释参见前文。 ↩

使用net use命令来临时挂载网络目录

2025-05-02 09:34:00

在Windows CMD中,如果无法直接cd到一个网络目录(专门些的名字叫——通用命名约定〈UNC〉路径),可以使用net use命令来临时挂载网络目录。以下是具体步骤12

  1. 使用net use命令挂载网络目录:
net use Z: \\network_path\shared_folder /user:username password
  • 其中:
    • Z: 是你希望分配给网络目录的本地驱动器号。
    • \\network_path\shared_folder是网络目录的路径。
    • /user:username password是访问网络目录所需的用户名和密码。
    • 挂载完毕后,不仅在CMD内,图形界面的“文件资源管理器”下“此电脑”中也可以看到该挂载目录。
  1. 访问挂载的网络目录:
Z: # 直接输入`Z:`便可切换至该挂载盘符
cd shared_folder # 然后用`cd`命令跳转
  1. 完成操作后卸载网络目录:
net use Z: /delete

补充:pushd命令

在Windows CMD中,pushd命令也可以用来临时挂载网络目录并切换到该目录(而且该方式还更常见一些)。具体步骤如下:

  1. 使用pushd命令挂载网络目录3
set unc_path=\\network_path\shared_folder\b\c
pushd %unc_path%
  1. 执行想要的代码;
  1. 完成操作后使用popd命令卸载网络目录:
popd

使用pushd命令时,它会将网络路径\\network_path\shared_folder挂载到一个可用的盘符(例如Z:),并将工作目录更改为Z:\b\c
但是,这种方法有一个致命的缺点:如果用户对\\network_path\shared_folder\b\c有权限,但对\\network_path\shared_folder没有权限,那么所有在此目录下的命令都会报错:Access is denied4

参考资料

  1. rwv. 在 Windows CMD 中挂载网络路径至本地盘符. rwv 的博客. ↩
  2. Windows掛載共享磁碟/網路磁碟(CIFS)指令. 平凡的幸福. ↩
  3. pushd. Microsoft Learn. ↩
  4. rwv. 在 Windows CMD 中挂载网络路径至本地盘符. rwv 的博客. ↩