<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Echo of Joy</title><description>No description</description><link>https://blog.gckjoy.com/</link><language>zh_CN</language><item><title>搭建一个临时邮件服务，支持收件和发件</title><link>https://blog.gckjoy.com/posts/temp-mail/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/temp-mail/</guid><pubDate>Sat, 29 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这里就不放演示地址了，直接看效果图：
&lt;img src=&quot;https://oss.zzii.de/images/2025/11/33208b344854f52aaa393031576d3072.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;1. 部署配置 (Docker Compose)&lt;/h3&gt;
&lt;p&gt;创建一个 &lt;code&gt;docker-compose.yml&lt;/code&gt; 文件并粘贴以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3.8&apos;

services:
  temp-mail:
    image: neixin/temp-mail:latest
    container_name: temp-mail
    restart: unless-stopped
    ports:
      - &quot;8080:8080&quot;   # HTTP Web/API 访问端口
      - &quot;25:25&quot;       # SMTP 邮件服务端口
    environment:
      - HTTP_ADDR=:8080
      - SMTP_ADDR=:25
      - DOMAIN=example.com   # ⚠️ 请修改为你的实际域名
      - MESSAGE_TTL=180s      # 邮件过期时间
      - TZ=Asia/Shanghai
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2. DNS 记录配置&lt;/h3&gt;
&lt;p&gt;这是最关键的一步，请根据下表配置域名解析（以 &lt;code&gt;example.com&lt;/code&gt; 为例）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;主机记录&lt;/th&gt;
&lt;th&gt;记录值&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;注意事项&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.2.3.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Web 访问入口&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;可开启 CF 代理&lt;/strong&gt; (小黄云)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.2.3.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;邮件服务器 IP&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;建议仅 DNS&lt;/strong&gt; (小灰云)，否则可能影响收信&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mx.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;指定邮件服务器&lt;/td&gt;
&lt;td&gt;优先级设为 10 即可&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TXT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v=spf1 a mx ~all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SPF 记录&lt;/td&gt;
&lt;td&gt;声明允许该服务器发信，防止被判为垃圾邮件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TXT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_dmarc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;v=DMARC1; p=none; rua=mailto:admin@example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DMARC 策略&lt;/td&gt;
&lt;td&gt;可选配置，建议添加&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;配置说明&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;请将 &lt;code&gt;1.2.3.4&lt;/code&gt; 替换为你服务器的真实 IP。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mx&lt;/code&gt; 记录对应的主机记录（如 &lt;code&gt;mx.example.com&lt;/code&gt;）&lt;strong&gt;不建议&lt;/strong&gt;开启 Cloudflare 的 CDN (小黄云)，因为免费版 CF 不代理 25 端口，会导致无法收到邮件。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;3. 项目源码&lt;/h3&gt;
&lt;p&gt;感兴趣的朋友可以去 Star 支持一下：
👉 &lt;strong&gt;Github 地址&lt;/strong&gt;：&lt;a href=&quot;https://github.com/Nei-Xin/temp_mail&quot;&gt;https://github.com/Nei-Xin/temp_mail&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;资源占用情况：
&lt;img src=&quot;https://oss.zzii.de/images/2025/11/3d3fe79a46d8b8515e463a8ca053bd52.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;【Warning】：改项目后端由Claude生成，前端由Gemini3生成，可能会有bug?&lt;/p&gt;
</content:encoded></item><item><title>使用 Graph SDK 获取 OneDrive 自定义年限 Client Secret 的 API</title><link>https://blog.gckjoy.com/posts/onedrive-api/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/onedrive-api/</guid><pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;通常情况下，我们都是直接在 Microsoft Azure 应用注册页面创建 OneDrive API。但是，通过网页界面创建的 API 密钥（Client Secret）最长有效期仅为 &lt;strong&gt;两年&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果是作为长期运行的服务（如 NAS 挂载、自动备份等），两年一换难免会因为忘记更新密钥而导致服务不可用。通过使用 Microsoft Graph PowerShell SDK，我们可以突破这个限制，自定义密钥的有效期（例如设置为 100 年），从而实现“永久”使用。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. 安装 Graph SDK&lt;/h2&gt;
&lt;p&gt;首先，需要在 PowerShell 中安装 Microsoft Graph 模块。请以管理员身份或当前用户身份运行 PowerShell 并执行以下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Install-Module Microsoft.Graph -Scope CurrentUser -Force -AllowClobber
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 登录账号并获取权限&lt;/h2&gt;
&lt;p&gt;安装完成后，需要登录你的微软账号并授予必要的权限以便通过命令行管理应用。&lt;/p&gt;
&lt;p&gt;执行以下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Connect-MgGraph -Scopes &quot;Application.ReadWrite.All&quot;, &quot;AppRoleAssignment.ReadWrite.All&quot;, &quot;Directory.ReadWrite.All&quot;

# 获取 Microsoft Graph 的服务主体引用
$graphSp = Get-MgServicePrincipal -Filter &quot;displayName eq &apos;Microsoft Graph&apos;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：执行 &lt;code&gt;Connect-MgGraph&lt;/code&gt; 后会弹出浏览器窗口，请登录你需要创建应用的微软账号并同意授权。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;3. 配置并创建应用&lt;/h2&gt;
&lt;p&gt;这是核心步骤。你可以根据需要修改下方的配置变量（如应用名称、密钥名称、有效期等）。将以下脚本复制到 PowerShell 中运行即可。&lt;/p&gt;
&lt;h3&gt;配置参数&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# === 基础配置 ===
$displayName = &quot;rclone&quot;                                   # 注册应用服务名称
$secretDisplayName = &quot;rclone-secret&quot;                      # 密钥名称
$secretValidityYears = 100                                # 期限，这里设置 100 年
$redirectUri = &quot;http://localhost&quot;                         # 重定向 URI
$signInAudience = &quot;AzureADandPersonalMicrosoftAccount&quot;    # 受支持的帐户类型（个人+组织）

# === 权限定义 ===
# 委托权限 (Delegated Permissions)
$delegatedPermissions = @(&quot;User.Read&quot;, &quot;Files.Read&quot;, &quot;Files.ReadWrite&quot;, &quot;offline_access&quot;)
# 应用程序权限 (Application Permissions)
$appPermissions = @(&quot;Files.Read.All&quot;, &quot;Files.ReadWrite.All&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;执行创建逻辑&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# === 查找权限 ID ===
$requiredDelegatedScopes = $graphSp.Oauth2PermissionScopes | Where-Object { $delegatedPermissions -contains $_.Value }
$requiredAppRoles = $graphSp.AppRoles | Where-Object { $appPermissions -contains $_.Value }

# === 构建权限结构 ===
$resourceAccessList = @()

# 添加委托权限
$requiredDelegatedScopes | ForEach-Object {
    $resourceAccessList += @{
        Id = $_.Id
        Type = &quot;Scope&quot;
    }
}

# 添加应用权限
$requiredAppRoles | ForEach-Object {
    $resourceAccessList += @{
        Id = $_.Id
        Type = &quot;Role&quot;
    }
}

$requiredResourceAccess = @(
    @{
        ResourceAppId = $graphSp.AppId
        ResourceAccess = $resourceAccessList
    }
)

# === 设置重定向 URI ===
$webRedirectUris = @($redirectUri)

# === 创建应用程序 ===
$app = New-MgApplication -DisplayName $displayName `
    -SignInAudience $signInAudience `
    -Web @{ RedirectUris = $webRedirectUris } `
    -RequiredResourceAccess $requiredResourceAccess

# === 创建服务主体 ===
$sp = New-MgServicePrincipal -AppId $app.AppId

# === 创建客户端密码凭据 (Client Secret) ===
$startDate = Get-Date
$endDate = $startDate.AddYears($secretValidityYears)

$secret = Add-MgApplicationPassword -ApplicationId $app.Id -PasswordCredential @{
    DisplayName = $secretDisplayName
    StartDateTime = $startDate
    EndDateTime = $endDate
}

# === 输出结果 ===
Write-Host &quot;------------------------------------------------&quot; -ForegroundColor Green
Write-Host &quot;应用创建成功！&quot; -ForegroundColor Green
Write-Host &quot;应用名称 (Name): $displayName&quot;
Write-Host &quot;Application (client) ID: $($app.AppId)&quot;
Write-Host &quot;Client Secret: $($secret.SecretText)&quot;
Write-Host &quot;------------------------------------------------&quot; -ForegroundColor Green
Write-Host &quot;请立即保存 Client Secret，它只显示一次。&quot; -ForegroundColor Yellow
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 结语&lt;/h2&gt;
&lt;p&gt;执行完上述脚本后，不出意外你应该已经成功获取到了 &lt;strong&gt;Client ID&lt;/strong&gt; 和 &lt;strong&gt;Client Secret&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Client ID&lt;/strong&gt;: 用于标识你的应用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client Secret&lt;/strong&gt;: 有效期为你设置的年限（如 100 年）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;如何切换账号？&lt;/h3&gt;
&lt;p&gt;如果你想为另一个账号创建 API，需要先注销当前登录，然后重复上述步骤：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 注销当前登录
Disconnect-MgGraph
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后再次运行 &lt;code&gt;Connect-MgGraph&lt;/code&gt; 即可登录新账号。&lt;/p&gt;
&lt;p&gt;转载自：https://pbpz.net/2025/10/03/5.html&lt;/p&gt;
</content:encoded></item><item><title>博客又㕛叒叕迁移了</title><link>https://blog.gckjoy.com/posts/blog-migration/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/blog-migration/</guid><pubDate>Thu, 30 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;博客又㕛叒叕迁移了，博客从halo迁移到静态博客了，同时删掉了一些我个人觉得没什么用的文章。
这段时间更新的越来越少了（主要是想更新，但不知道更新什么..），所以就迁移到静态博客了，起码不占用服务器资源了。
博客经历历程：静态-&amp;gt;wordpress-&amp;gt;typecho-&amp;gt;halo-&amp;gt;静态，所以博客的终点是静态博客?
不过还是希望后面可以坚持更新下去吧&lt;/p&gt;
</content:encoded></item><item><title>Docker安装mysql，并配置两台服务器主主复制</title><link>https://blog.gckjoy.com/posts/docker-mysql-cluster/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/docker-mysql-cluster/</guid><pubDate>Mon, 03 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;安装mysql&lt;/h3&gt;
&lt;p&gt;在两台服务器上同时进行，这里以mysql 5.7为例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run -d \
--name mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=sadffsafdsfaf \
mysql:5.7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后将配置文件复制出来&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker cp mysql:/etc/mysql /root/data/mysql/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;docker compose启动mysql&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3.9&apos;
services:
    mysql:
        image: &apos;mysql:5.7&apos;
        environment:
            - MYSQL_ROOT_PASSWORD=sadffsdadfsdfa
        volumes:
            - &apos;./data:/var/lib/mysql&apos;
            - &apos;./config:/etc/mysql&apos;
        ports:
            - &apos;3306:3306&apos;
        container_name: mysql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在server1上，进入config文件夹，创建&lt;code&gt;my.cnf&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[mysqld]
server-id=1
log-bin=mysql-bin
binlog_format=ROW
relay-log=relay-bin
auto_increment_increment=2
auto_increment_offset=1
bind-address=0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在server2上，进入config文件夹，创建&lt;code&gt;my.cnf&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[mysqld]
server-id=2
log-bin=mysql-bin
binlog_format=ROW
relay-log=relay-bin
auto_increment_increment=2
auto_increment_offset=2
bind-address=0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动mysql&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;配置mysql主主复制&lt;/h3&gt;
&lt;h4&gt;创建复制用户&lt;/h4&gt;
&lt;p&gt;在&lt;strong&gt;两台服务器&lt;/strong&gt;上运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;
GRANT REPLICATION SLAVE ON *.* TO &apos;repl&apos;@&apos;%&apos; IDENTIFIED BY &apos;repl_password&apos;;
FLUSH PRIVILEGES;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取主库的二进制日志信息&lt;/h4&gt;
&lt;p&gt;在server 1上&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;SHOW MASTER STATUS;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      599 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;记录 &lt;code&gt;File&lt;/code&gt; 和 &lt;code&gt;Position&lt;/code&gt;，稍后用于 Server 2 配置&lt;/p&gt;
&lt;h4&gt;配置 Server 2 复制 Server 1&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;
CHANGE MASTER TO MASTER_HOST=&apos;192.168.1.10&apos;, 
MASTER_USER=&apos;repl&apos;, 
MASTER_PASSWORD=&apos;repl_password&apos;, 
MASTER_LOG_FILE=&apos;mysql-bin.000001&apos;, 
MASTER_LOG_POS=154;
START SLAVE;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;获取 Server 2 的二进制日志信息&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;SHOW MASTER STATUS;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      599 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;记录 &lt;code&gt;File&lt;/code&gt; 和 &lt;code&gt;Position&lt;/code&gt;，稍后用于 Server 1 配置&lt;/p&gt;
&lt;h4&gt;配置 Server 1 复制 Server 2&lt;/h4&gt;
&lt;p&gt;在 &lt;strong&gt;Server 1（mysql1）&lt;/strong&gt; 上执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;
CHANGE MASTER TO MASTER_HOST=&apos;192.168.1.10&apos;, 
MASTER_USER=&apos;repl&apos;, 
MASTER_PASSWORD=&apos;repl_password&apos;, 
MASTER_LOG_FILE=&apos;mysql-bin.000001&apos;, 
MASTER_LOG_POS=154;
START SLAVE;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;检查主主复制状态&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;SHOW SLAVE STATUS\G&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;确保 &lt;code&gt;Slave_IO_Running&lt;/code&gt; 和 &lt;code&gt;Slave_SQL_Running&lt;/code&gt; 都是 &lt;strong&gt;Yes&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Seconds_Behind_Master&lt;code&gt;应该为&lt;/code&gt;0&lt;code&gt;或者接近&lt;/code&gt;0&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;测试主主复制状态&lt;/h3&gt;
&lt;p&gt;在server 1上插入数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;CREATE DATABASE test_db; USE test_db; CREATE TABLE test_table (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50)); INSERT INTO test_table (name) VALUES (&apos;Server1&apos;);&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在server 2上查询数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it mysql mysql -uroot -p -e &quot;SELECT * FROM test_db.test_table;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果数据同步成功，说明 &lt;strong&gt;主主复制已正确配置！🎉&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>Python虚拟环境的简单使用教程</title><link>https://blog.gckjoy.com/posts/python-venv/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/python-venv/</guid><pubDate>Sun, 30 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;使用 &lt;code&gt;venv&lt;/code&gt; 创建和管理虚拟环境&lt;/h1&gt;
&lt;h2&gt;介绍&lt;/h2&gt;
&lt;p&gt;最近在将大部分服务器从debian11升级到debian12后，安装python库时，会提示用户需要在venv虚拟环境中安装python库，所以记录一下python创建和使用venv虚拟环境。&lt;/p&gt;
&lt;h2&gt;1. 创建虚拟环境&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;python3&lt;/code&gt; 命令创建虚拟环境，并将其放置在当前目录下的 &lt;code&gt;.env&lt;/code&gt; 目录中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m venv .env
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 激活虚拟环境&lt;/h2&gt;
&lt;p&gt;在 macOS 和 Linux 系统中，可以通过以下命令激活虚拟环境：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source .env/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Windows 系统中，可以使用以下命令激活虚拟环境：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.env\Scripts\activate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;激活后，你会在命令行提示符中看到虚拟环境的名称，例如 &lt;code&gt;(.env)&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;3. 安装依赖包&lt;/h2&gt;
&lt;p&gt;在虚拟环境激活的情况下，使用 &lt;code&gt;pip&lt;/code&gt; 安装你需要的依赖包：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install 包名
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 列出已安装的包&lt;/h2&gt;
&lt;p&gt;你可以使用以下命令来列出虚拟环境中已安装的所有包：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip list
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 生成 &lt;code&gt;requirements.txt&lt;/code&gt; 文件&lt;/h2&gt;
&lt;p&gt;为了记录当前虚拟环境中的所有依赖包，可以生成一个 &lt;code&gt;requirements.txt&lt;/code&gt; 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 使用 &lt;code&gt;requirements.txt&lt;/code&gt; 安装依赖包&lt;/h2&gt;
&lt;p&gt;在新的环境中，你可以通过以下命令安装 &lt;code&gt;requirements.txt&lt;/code&gt; 文件中列出的所有依赖包：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;7. 退出虚拟环境&lt;/h2&gt;
&lt;p&gt;当你完成工作后，可以通过以下命令退出虚拟环境：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deactivate
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. 删除虚拟环境&lt;/h2&gt;
&lt;p&gt;如果你不再需要虚拟环境，可以简单地删除虚拟环境的目录：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rm -rf .env
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>搭建一个简单的随机图片API，支持Docker部署</title><link>https://blog.gckjoy.com/posts/docker-random-pic-api/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/docker-random-pic-api/</guid><pubDate>Sun, 26 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;继前言：&lt;a href=&quot;https://blog.gckjoy.com/archives/random-pic-api&quot;&gt;随机图片api搭建教程&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;更新&lt;/h3&gt;
&lt;h4&gt;2024.5.27&lt;/h4&gt;
&lt;h5&gt;新增&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;/pc路径，显示横屏图片，例如：https://api.neix.in/random/pc
&lt;img src=&quot;https://api.neix.in/random/pc&quot; alt=&quot;https://api.neix.in/random/pc&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/mobile，显示竖屏图片，例如：https://api.neix.in/random/mobile&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://api.neix.in/random/mobile&quot; alt=&quot;https://api.neix.in/random/mobile&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;镜像大小减小了&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;项目地址&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Nei-Xin/random-pic-api&quot;&gt;https://github.com/Nei-Xin/random-pic-api&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;简介&lt;/h4&gt;
&lt;p&gt;随机图片 API 是一种允许开发者从一个图片库或者指定的目录中获取随机图片的接口。这种 API 通常用于网站、移动应用程序或其他软件中，以便动态地展示随机图片，例如用作背景图片、占位图、或者其他需要随机化内容的场景。&lt;/p&gt;
&lt;h3&gt;在线体验&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://api.neix.in/random/&quot;&gt;https://api.neix.in/random/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://api.neix.in/random/&quot; alt=&quot;https://api.neix.in/random/&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;特性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;图片随机展示&lt;/li&gt;
&lt;li&gt;设备适配：通过检测用户代理字符串，判断访问设备是手机还是电脑，并根据设备类型选择对应的图片文件夹路径。&lt;/li&gt;
&lt;li&gt;图片格式支持：web,jpg,jpeg,png,gif&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;部署&lt;/h3&gt;
&lt;h4&gt;PHP&lt;/h4&gt;
&lt;p&gt;直接丢到有PHP和Nginx的环境中就行&lt;/p&gt;
&lt;h4&gt;Docker&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3.9&apos;
services:
    random-api:
        image: &apos;neixin/random-pic-api&apos;
        volumes:
			# 竖屏图片
            - &apos;./portrait:/var/www/html/portrait&apos;
            # 横屏图片
            - &apos;./landscape:/var/www/html/landscape&apos;
        ports:
            - &apos;8080:80&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;图片处理&lt;/h3&gt;
&lt;h4&gt;代码&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;from PIL import Image
import os

# 检查图片方向
def get_image_orientation(image_path):
    with Image.open(image_path) as img:
        width, height = img.size
        return &quot;landscape&quot; if width &amp;gt; height else &quot;portrait&quot;

# 转换图片为 WebP 格式
def convert_to_webp(image_path, output_folder, max_pixels=178956970):
    try:
        with Image.open(image_path) as img:
            # Check image size
            width, height = img.size
            if width * height &amp;gt; max_pixels:
                print(f&quot;Skipping {image_path} because it exceeds the size limit.&quot;)
                return
            
            # Save the image as WebP
            output_path = os.path.join(output_folder, os.path.splitext(os.path.basename(image_path))[0] + &quot;.webp&quot;)
            img.save(output_path, &quot;webp&quot;)
    except Exception as e:
        print(f&quot;Failed to convert {image_path}: {e}&quot;)

# 遍历文件夹中的图片
def process_images(input_folder, output_folder_landscape, output_folder_portrait):
    for filename in os.listdir(input_folder):
        if filename.endswith((&apos;.jpg&apos;, &apos;.jpeg&apos;, &apos;.png&apos;)):
            image_path = os.path.join(input_folder, filename)
            orientation = get_image_orientation(image_path)
            try:
                if orientation == &quot;landscape&quot;:
                    convert_to_webp(image_path, output_folder_landscape)
                else:
                    convert_to_webp(image_path, output_folder_portrait)
            except Exception as e:
                print(f&quot;Error processing {image_path}: {e}. Skipping this image.&quot;)

# 指定输入和输出文件夹
input_folder = &quot;/root/photos&quot;
output_folder_landscape = &quot;/root/landscape&quot;
output_folder_portrait = &quot;/root/portrait&quot;

# 执行转换
process_images(input_folder, output_folder_landscape, output_folder_portrait)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;作用&lt;/h4&gt;
&lt;p&gt;将横屏和竖屏的图片分开，并转化为webp格式，使用时注意修改文件路径&lt;/p&gt;
&lt;h3&gt;最后&lt;/h3&gt;
&lt;p&gt;如果觉得还不错的话，可以点个star&lt;/p&gt;
</content:encoded></item><item><title>Screen 使用教程</title><link>https://blog.gckjoy.com/posts/screen-use/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/screen-use/</guid><pubDate>Thu, 16 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;Screen是一款由GNU开发的用于命令行终端切换的自由软件。用户可以通过该软件同时连接多个本地或远程的命令行会话，并自由切换。&lt;/p&gt;
&lt;h2&gt;使用教程&lt;/h2&gt;
&lt;h3&gt;安装Screen&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CentOS/RedHat/Fedora&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yum -y install screen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ubuntu/Debian&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get -y install screen
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;参数&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;-A 　将所有的视窗都调整为目前终端机的大小。
-d     &amp;lt;作业名称&amp;gt; 　将指定的screen作业离线。
-h     &amp;lt;行数&amp;gt; 　指定视窗的缓冲区行数。
-m 　即使目前已在作业中的screen作业，仍强制建立新的screen作业。
-r      &amp;lt;作业名称&amp;gt; 　恢复离线的screen作业。
-R 　先试图恢复离线的作业。若找不到离线的作业，即建立新的screen作业。
-s 　指定建立新视窗时，所要执行的shell。
-S    &amp;lt;作业名称&amp;gt; 　指定screen作业的名称。
-v 　显示版本信息。
-x 　恢复之前离线的screen作业。
-ls或--list 　显示目前所有的screen作业。
-wipe 　检查目前所有的screen作业，并删除已经无法使用的screen作业。
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;常用参数&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;screen -S session_name           # 新建一个叫session_name的session
screen -ls（或者screen -list）   # 列出当前所有的session
screen -r session_name            # 回到session_name这个session
screen -d session_name           # 远程detach某个session
screen -d -r session_name        # 结束当前session并回到session_name这个session
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;在每个screen session 下，命令都以 ctrl+a、ctrl-a，常用的几个操作如下：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ctrl-a x   # 锁住当前的shell window，需用用户密码解锁
ctrl-a d   # detach，暂时离开当前session，将当前 screen session 转到后台执行，并会返回没进 screen 时的状态，此时在 screen session 里，每个shell client内运行的 process (无论是前台/后台)都在继续执行，即使 logout 也不影响
ctrl-a z   # 把当前session放到后台执行，用 shell 的 fg 命令则可回去。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;具体用法&lt;/h3&gt;
&lt;h4&gt;启动一个会话&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;screen
# 创建会话并命名
screen -S name
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;从会话中推出&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Ctrl + a + d
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;重新连接screen&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# 只有一个会话
screen -r
# 如果有多个会话
screen -ls

There are screens on:
7880.session    (Detached)
7934.session2   (Detached)
7907.session1   (Detached)
3 Sockets in /var/run/screen/S-root.

screen -r 7934

# 或者使用名称
screen -r -S session2
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;中止screen会话&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# 使用-R/-r/-S均可
screen -R [pid/Name] -X quit
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>搭建一个随机图片api</title><link>https://blog.gckjoy.com/posts/random-pic-api/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/random-pic-api/</guid><pubDate>Sat, 20 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h4&gt;图片分类，并转化为webp格式&lt;/h4&gt;
&lt;p&gt;最近在搭建随机图片api过程中，想到将图片横屏和竖屏区分开，这样更适配手机和电脑，并将图片转化为webp格式，可以加载的更快，注意修改图片路径，python代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from PIL
import Image
import os

# 检查图片方向
def get_image_orientation(image_path):
    with Image.open(image_path) as img:
        width, height = img.size
        return &quot;landscape&quot; if width &amp;gt; height else &quot;portrait&quot;

# 转换图片为 WebP 格式
def convert_to_webp(image_path, output_folder):
    with Image.open(image_path) as img:
        output_path = os.path.join(output_folder, os.path.splitext(os.path.basename(image_path))[0] + &quot;.webp&quot;)
        img.save(output_path, &quot;webp&quot;)

# 遍历文件夹中的图片
def process_images(input_folder, output_folder_landscape, output_folder_portrait):
    for filename in os.listdir(input_folder):
        if filename.endswith((&apos;.jpg&apos;, &apos;.jpeg&apos;, &apos;.png&apos;)):
            image_path = os.path.join(input_folder, filename)
            orientation = get_image_orientation(image_path)
            if orientation == &quot;landscape&quot;:
                convert_to_webp(image_path, output_folder_landscape)
            else:
                convert_to_webp(image_path, output_folder_portrait)

# 指定输入和输出文件夹
input_folder = &quot;/root/data/alist/local/photos&quot;
output_folder_landscape = &quot;/root/data/alist/local/landscape&quot;
output_folder_portrait = &quot;/root/data/alist/local/portrait&quot;

# 执行转换
process_images(input_folder, output_folder_landscape, output_folder_portrait)

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;php代码&lt;/h4&gt;
&lt;p&gt;可以自动识别手机端和电脑端，创建landscape和portrait文件夹，分别存放横屏和竖屏图片&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
// 设置站点地址
$weburl = &apos;https://api.zzii.de/&apos;;
$pcPath = &apos;landscape&apos;;
$mobilePath = &apos;portrait&apos;;

// 函数：从目录中获取图片列表
function getImagesFromDir($path) {
    $images = array();
    if ($img_dir = @opendir($path)) {
        while (false !== ($img_file = readdir($img_dir))) {
            if (preg_match(&quot;/(\.webp)$/&quot;, $img_file)) { // 仅匹配 WebP 格式的图片
                $images[] = $img_file;
            }
        }
        closedir($img_dir);
    }
    return $images;
}

// 检测用户代理以区分手机和电脑访问
$userAgent = $_SERVER[&apos;HTTP_USER_AGENT&apos;];
$isMobile = preg_match(&apos;/(android|iphone|ipad|ipod|blackberry|windows phone)/i&apos;, $userAgent);

// 根据访问设备设置图片路径
if ($isMobile) {
    $path = $mobilePath;
} else {
    $path = $pcPath;
}

// 缓存图片列表
$imgList = getImagesFromDir($path);

// 从列表中随机选择一张图片
shuffle($imgList);
$img = reset($imgList);

// 直接输出所选的随机图片
$img_path = $path . &apos;/&apos; . $img;
$img_url = $weburl . $img_path;
header(&apos;Content-Type: image/webp&apos;); // 设置 Content-Type 为 image/webp
readfile($img_path);
?&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;最后&lt;/h4&gt;
&lt;p&gt;给一个我搭建的成果吧：&lt;a href=&quot;https://api.neix.in/random/&quot;&gt;https://api.neix.in/random/&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Git使用教程</title><link>https://blog.gckjoy.com/posts/git-use/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/git-use/</guid><pubDate>Sat, 16 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;初始化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git config --global user.name &quot;William-ku&quot;

git config --global user.email oyaldeer@gmail.com

git config --global credential.helper store

git config --global --list
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;创建仓库&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git init

git init my-repo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从远程仓库拉取仓库&lt;/p&gt;
&lt;h3&gt;Git的三种状态&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;工作区，在资源管理器里面能够看到的文件夹就是工作区&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;暂存区，临时存储区域，用于保存即将提交到Git仓库的修改内容&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本地仓库，git init 生成的仓库&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;提交到仓库&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;git init 创建文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git status 查看仓库的状态&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git add 添加到暂存区&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git commit -m &quot;初始化&quot; 提交&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git log 查看仓库提交历史记录&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;git reset的三种模式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;git reset --soft&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git reset --hard&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git reset --mixed&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;git diff&lt;/h3&gt;
&lt;p&gt;查看工作区、暂存区、本地仓库之间的差异&lt;/p&gt;
&lt;h3&gt;删除文件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;rm file;git add file 先从工作区删除文件，然后再暂存区删除内容&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git rm &amp;lt;file&amp;gt; 把文件从工作区和暂存区同时删除&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git rm --cached &amp;lt;file&amp;gt; 把文件从暂存区删除，但保留在当前工作区中&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git rm -r * 递归删除某个目录下的所有子目录和文件，递归后不要忘记提交&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;.gitignore文件&lt;/h3&gt;
&lt;p&gt;不提交到远程仓库的文件&lt;/p&gt;
&lt;h3&gt;SSH配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ssh-keygen -t rsa -b 4096
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;关联本地仓库和远程仓库&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git remote add origin git@github.com:William-ku/test3.git

git branch -M main

git push -u origin main

git remote -v // 查看关联情况

git pull
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;分支&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;git branch dev 创建分支&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git bramch 查看分支&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git switch dev 切换分支&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git branch -d master 删除分支&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;参考资料&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1HM411377j?p=19&amp;amp;spm_id_from=pageDriver&amp;amp;vd_source=1354c01c1e256a9aae46bd39847192d3&quot;&gt;【GeekHour】一小时Git教程&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>vim入门教程</title><link>https://blog.gckjoy.com/posts/vim-study/</link><guid isPermaLink="true">https://blog.gckjoy.com/posts/vim-study/</guid><pubDate>Wed, 13 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;介绍&lt;/h3&gt;
&lt;p&gt;Vim 是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富，在程序员中被广泛使用。简单的来说， vi 是老式的字处理器，不过功能已经很齐全了，但是还是有可以进步的地方。 vim 则可以说是程序开发者的一项很好用的工具。&lt;/p&gt;
&lt;h3&gt;固定配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;vim ~/.vimrc
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;syntax on

set number
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常用指令&lt;/h3&gt;
&lt;h6&gt;代码高亮&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;:syntax on
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;显示行号&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;:set nu
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;光标移动&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;h 左

j 下

k 上

l 右
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;跳过一个单词&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;w 往前跳

b 往后跳
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;翻页&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;page up , ctrl f;

page down , ctrl b;
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;跳到某一行&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;digit gg;

digit G;
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;往下跳行&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;10j 往下跳10行;

10k 往上跳10行;
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;搜索&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;/Node 搜索;

n 跳到下一个;

shift n 跳到上一个;
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;删除一行&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;cc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;删除2行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c2c
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;撤销&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;u
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;粘贴&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;p
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;删除整段&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;v 选中整段后;

c 删除
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;复制一行&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;yy
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;自动补全&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;ctrl n
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>