根域名(Root Zone)与 顶级域名(Top-Level Domain, TLD)的 DNS 解析

顶级域名

例如:.nl顶级域名(Top-Level Domain, 简称 TLD)。


✅ 详细解释:

🌐 什么是顶级域名(TLD)?

顶级域名是域名系统(DNS)中最高层级的域名之一,位于根域之下。它通常表示一个国家、地区或类别。

例如:

  • .com.org.net → 通用顶级域名(gTLD)
  • .cn(中国)、.uk(英国)、.de(德国)→ 国家代码顶级域名(ccTLD)
  • .nl(荷兰)→ 也是 国家代码顶级域名(country code Top-Level Domain, ccTLD)

.nl 中的 nl 是“Netherlands”(荷兰)的国家代码。


❌ 为什么不是“根域名”?

  • 根域名(Root Zone)指的是 DNS 层级中的最顶层,用一个空标签或点表示(即 .),比如 example.com. 后面的那个点。
  • 根域名本身不直接供公众注册使用,它管理所有顶级域名(如 .com, .org, .nl 等)。
  • 所有 TLD 都隶属于根域名之下。

📚 域名层级示例:www.example.nl

.        ← 根域名(Root)
└── nl   ← 顶级域名(TLD,国家代码)
    └── example  ← 二级域名(由 .nl 注册局管理)
        └── www  ← 主机名(子域名)

✅ 总结

名称类型说明
.nl顶级域名(TLD)荷兰的国家代码顶级域名(ccTLD)
.根域名DNS 最顶层,不可注册

👉 所以答案是:.nl顶级域名,不是根域名。


反过来理解

域名从解析层面来讲是反过来的:
. 根域名(Root)) → nl 顶级域名(TLD,国家代码) → example 二级域名(由 .nl 注册局管理) → www 主机名(子域名)

这个点 “ . ”非常重要,它就是根(Root)

是的,非常正确!👏

DNS 解析的查询过程 来讲,域名确实是 “反过来”逐级查找的,即从根开始,向下一级一级递归解析。你描述的方向完全符合 DNS 的实际工作原理。


✅ 正确的 DNS 解析顺序(由根向叶子):

以访问 www.example.nl 为例,DNS 解析流程如下:

Step 1: 询问根域名服务器(Root Servers)
        "谁管理 .nl 域?"
        ↓
Step 2: 根服务器回复:
        "去找负责 .nl 的顶级域名服务器(TLD nameservers)"
        ↓
Step 3: 询问 .nl 的 TLD 域名服务器
        "谁管理 example.nl?"
        ↓
Step 4: .nl 注册局的服务器回复:
        "去找 example.nl 的权威域名服务器(Authoritative Nameservers)"
        ↓
Step 5: 询问 example.nl 的权威服务器
        "www.example.nl 的 IP 是什么?"
        ↓
Step 6: 权威服务器返回:
        "www.example.nl 的 IP 是 93.184.216.34"

🌐 所以从逻辑结构上看:

虽然我们写域名是:
👉 www.example.nl (从左到右)

但在 DNS 层级和解析路径中,它是:
👉 . → .nl → example.nl → www.example.nl
也就是从 右到左从根到叶 的树状结构。

.
└── nl                          ← TLD(顶级域名),由荷兰 SIDN 管理
    └── example                 ← 二级域名(注册者购买)
        └── www                 ← 子域名 / 主机名

🔁 因此:书写顺序是从左到右,但解析顺序是从右到左,自顶向下。


🧠 类比理解

就像文件路径:

  • Windows: C:\Users\Alice\Documents\file.txt
  • Linux: /home/alice/docs/file.txt

路径是从根 / 开始,逐级进入子目录。同样地,DNS 域名也是从根 . 开始,逐级进入更具体的域。


✅ 总结

说法是否正确说明
.nl 是顶级域名荷兰的国家代码顶级域名(ccTLD)
根域名是 .DNS 层级的最顶层
域名解析是从根开始,逐级向下即:..nlexample.nlwww.example.nl
写作顺序与解析路径相反我们从左写到右,但系统从右查到左

✅ 所以你的理解完全正确:

DNS 解析是从根域名开始,一步步“向下”查找,直到找到最终主机名的 IP 地址。

这正是互联网域名系统设计的核心机制 —— 分层、分布式、可扩展。

Windows 10 简体中文补充字体 无法添加 0x80070490

最近系统持续多次出现,通知中心出现一个图标带“字”的通知,提示安装失败的通知。点击会打开设置,也不知道要作甚吗。一番查阅后才解决:

简体中文补充字体 (Chinese (Simplified) Supplemental Fonts) 安装失败的问题,验证,在 设置→系统→可选组件 中找不到简体中文补充字体。

  1. 先以管理员模式打开CMD命令行,进入 Powershell : powershell.
  2. 获取组件名称: Get-WindowsCapability -online -name *fonts* | ft Name, DisplayName, Description 可以得知名称为 Language.Fonts.Hans~~~und-HANS~0.0.1.0
  3. 再确认下该组件的状态: Get-WindowsCapability -online -name Language.Fonts.Hans~~~und-HANS~0.0.1.0 可得知状态是 Superseded (State : Superseded)
  4. 删除该组件: Remove-WindowsCapability -Online -Name Language.Fonts.Hans~~~und-HANS~0.0.1.0
  5. 重新安装该组件Add-WindowsCapability -Online -Name Language.Fonts.Hans~~~und-HANS~0.0.1.0

看到 State : Installed (已安装)

在 设置 → 系统→ 可选组件 中,可看到 “简体中文补充字体” 已添加。

重启系统完成。

设置XFCE4 桌面不自启动(默认进入命令行模式)

在 Debian 系统下,让 XFCE4 桌面不自启动(默认进入命令行模式),但需要时仍能手动启动它,可以通过以下方法实现:


方法 1:修改默认运行级别(推荐)

修改 systemd 目标(target),使系统默认启动到 多用户命令行模式(multi-user.target,需要时再手动启动 graphical.target(图形界面)。

步骤:

  1. 禁用图形界面自启动
   sudo systemctl set-default multi-user.target

(这会修改 /etc/systemd/system/default.target 链接)

  1. 需要时手动启动 XFCE4 桌面
   sudo systemctl start graphical.target

(或直接启动 lightdm 显示管理器)

   sudo systemctl start lightdm
  1. 恢复默认图形界面启动(可选):
   sudo systemctl set-default graphical.target

方法 2:禁用 LightDM 自动登录

如果系统使用 LightDM 作为显示管理器,可以修改其配置,使其不自动登录 XFCE4。

步骤:

  1. 编辑 /etc/lightdm/lightdm.conf
   sudo nano /etc/lightdm/lightdm.conf

修改以下内容:

   [Seat:*]
   autologin-user=  # 留空
   autologin-user-timeout=0
  1. 重启 LightDM
   sudo systemctl restart lightdm

(系统启动后会停留在登录界面,手动选择 XFCE4 登录)


方法 3:修改 GRUB 启动参数

在 GRUB 引导时手动选择是否进入图形界面。

步骤:

  1. 编辑 /etc/default/grub
   sudo nano /etc/default/grub

修改 GRUB_CMDLINE_LINUX_DEFAULT 行:

   GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"

改为:

   GRUB_CMDLINE_LINUX_DEFAULT="quiet splash text"  # 默认进入文本模式

或:

   GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"  # 默认进入图形界面
  1. 更新 GRUB 配置
   sudo update-grub
  1. 重启后,在 GRUB 菜单选择 Advanced options,手动选择 multi-user.targetgraphical.target

方法 4:直接禁用 XFCE4 会话

如果只想临时关闭 XFCE4,但不影响系统默认行为,可以:

pkill xfce4-session  # 直接关闭 XFCE4

或:

systemctl isolate multi-user.target  # 切换到命令行模式

总结

方法适用场景优点缺点
systemctl set-default multi-user.target长期禁用图形界面最干净,不影响系统服务需要手动 start graphical.target
修改 LightDM 配置防止自动登录可保留图形界面仍需选择 XFCE4 登录
修改 GRUB 参数临时切换灵活需重启生效
pkill xfce4-session临时关闭立即生效重启后恢复

推荐使用 方法 1(修改默认运行级别),既保证系统启动高效,又能在需要时手动启动 XFCE4。

Linux安装微软命令行文本编辑器-Microsoft Edit

微软干了一件好事!2025 年 5 月 18 日,Microsoft 发布了用 Rust 编程语言编写的编辑器的开源重建版本,简称为 Edit,适用于现代版本的 Windows。版本号未继续,重置从1.0.0开始,目前已经是1.2.0

Microsoft Edit 是 MS-DOS 的怀旧风格, 以widnows Dos 的操作习惯,解决了Linux shell 中编辑文本痛苦的大问题。

目前Microsoft Edit已支持包括FreeBSD在内的多种Unix-like系统,但Debian官方仓库尚未收录该软件包。只能通过下载安装文件的方式安装,安装过程中若遇到网络问题,可能需要配置合适的软件源镜像。

Linux 下安装脚本(Ubuntu、Debian 和 Linux Mint):

# 安装 Zstandard 
apt install zstd

# 下载软件包
wget https://github.com/microsoft/edit/releases/download/v1.2.0/edit-1.2.0-x86_64-linux-gnu.tar.zst

# 解压缩到用户的当前目录
tar xvf edit-1.2.0-x86_64-linux-gnu.tar.zst

# 将其移动到 bin 目录中以便随时访问,请相应调整路径
mv edit /usr/local/bin/edit

# 查看版本号
edit -v

# 启动编辑器
edit

终端输入edit命令即可启动编辑器

edit --version 查看版本

Windows 下 使用 winget 安装

winget install Microsoft.Edit

常用快捷键

New File:Ctrl+N
Open File:Ctrl+0
Save:Ctrl+s
Close Editor:Ctrl+W
Exit:Ctrl+Q
Undo:Ctrl+Z
Redo:Ctrl+Y
Cut:Ctrl+x
Copy:Ctrl+c
Paste:Ctrl+V
Find:Ctrl+F
Replace:Ctrl+R

Debian 系统上安装 rqlited

在 Debian 系统上安装 rqlited 可通过以下步骤完成:

  1. 下载预编译二进制文件
    rqlite 官方提供 Linux 平台的预编译二进制文件,可直接下载运行:

wget https://github.com/rqlite/rqlite/releases/download/v7.0.0/rqlited-v7.0.0-linux-amd64.tar.gz
tar -xzvf rqlited-v7.0.0-linux-amd64.tar.gz
cd rqlited-v7.0.0-linux-amd64

  1. 运行 rqlited
    单节点模式:
    ./rqlited -node-id 1 ~/rqlite/data
    集群模式(需指定 Leader 节点):

Leader 节点

./rqlited -node-id 1 -http-addr 192.168.1.100:4001 ~/rqlite/leader_data

Follower 节点(加入集群)

./rqlited -node-id 2 -http-addr 192.168.1.101:4001 -join http://192.168.1.100:4001 ~/rqlite/follower_data

  1. 验证安装
    通过 HTTP API 检查服务状态:

curl http://localhost:4001/status

  1. 可选配置
    数据目录权限:确保运行用户对数据目录有读写权限。
    系统服务化:通过 systemd 管理服务(示例配置):

sudo nano /etc/systemd/system/rqlited.service
内容参考:

[Unit]
Description=rqlited
After=network.target

[Service]
ExecStart=/path/to/rqlited -node-id 1 /path/to/data
User=rqlite
Restart=always

[Install]
WantedBy=multi-user.target


启用服务:

sudo systemctl enable --now rqlited

  1. 其他安装方式
    Docker 部署:
    docker run -p 4001:4001 rqlite/rqlited
    源码编译(需 Go 环境):
    git clone https://github.com/rqlite/rqlite
    cd rqlite
    make
    注意事项
    确保防火墙开放 4001(HTTP API)和 4002(Raft 通信)端口。
    生产环境建议配置 TLS 加密和认证。
    如需更详细的集群配置或性能优化,可参考官方文档。

附带参考设置示例脚本,通过变量定义数据存储目录,并根据 -node-id 自动生成目录路径:

deploy_rqlite_cluster.sh


#!/bin/bash
# 配置变量(根据实际环境修改)
BASE_DATA_DIR="/opt/rqlite"          # 数据存储基础目录
LEADER_IP="192.168.1.100"            # 主节点IP
FOLLOWER1_IP="192.168.1.101"         # 子节点1 IP
FOLLOWER2_IP="192.168.1.102"         # 子节点2 IP
READONLY_IP="192.168.1.103"          # 只读节点IP

# 创建数据目录(所有节点)
mkdir -p $BASE_DATA_DIR/{data1,data2,data3,data4}

# 主节点(Leader)
ssh $LEADER_IP "nohup ./rqlited -node-id 1 \\
  -http-addr $LEADER_IP:4001 \\
  -raft-addr $LEADER_IP:4002 \\
  $BASE_DATA_DIR/data1 > $BASE_DATA_DIR/rqlite.log 2>&1 &"

# 子节点1(Follower)
ssh $FOLLOWER1_IP "nohup ./rqlited -node-id 2 \\
  -http-addr $FOLLOWER1_IP:4001 \\
  -raft-addr $FOLLOWER1_IP:4002 \\
  -join http://$LEADER_IP:4001 \\
  $BASE_DATA_DIR/data2 > $BASE_DATA_DIR/rqlite.log 2>&1 &"

# 子节点2(Follower)
ssh $FOLLOWER2_IP "nohup ./rqlited -node-id 3 \\
  -http-addr $FOLLOWER2_IP:4001 \\
  -raft-addr $FOLLOWER2_IP:4002 \\
  -join http://$LEADER_IP:4001 \\
  $BASE_DATA_DIR/data3 > $BASE_DATA_DIR/rqlite.log 2>&1 &"

# 只读节点(Non-Voter)
ssh $READONLY_IP "nohup ./rqlited -node-id 4 \\
  -http-addr $READONLY_IP:4001 \\
  -raft-addr $READONLY_IP:4002 \\
  -non-voter \\
  -join http://$LEADER_IP:4001 \\
  $BASE_DATA_DIR/data4 > $BASE_DATA_DIR/rqlite.log 2>&1 &"

echo "集群部署完成,检查状态:"
echo "curl $LEADER_IP:4001/status?pretty"

脚本说明:

1. 通过BASE_DATA_DIR变量集中管理存储路径

2. 自动按node-id生成data1~data4子目录

3. 日志统一输出到基础目录下

4. 需提前确保各节点已安装rqlited

解决 wsl 重启后 /etc/resolv.conf 中的DNS丢失

今天在wsl中不知道操作了什么,重启后apt更新报错,发现resolv.conf的DNS变成了“127.0.0.1”,然后用 echo 写入 DNS,重启后又是没有了。

按微软的文档,设置修改 /etc/wsl.conf

[network]
generateResolvConf = false

然后写入 DNS 到 resolv.conf

echo "nameserver 192.168.1.1" > /etc/resolv.conf
echo "nameserver 114.114.115.115" > /etc/resolv.conf

重启后,一样,添加的nameserver没有了。

在windows中,直接编辑 \wsl.localhost\Debian\etc\resolv.conf

提示“系统无法辨识文件名”,无法直接修改。

看了服务,猜测是不是某些服务控制修改的,如:resolvconf,rdnssd,systemd-resolved,先关闭启动:

systemctl disable --now resolvconf.service rdnssd.service systemd-resolved.service

再次 echo DNS 到 resolv.conf, 重启后,还是不行。。。

无奈,直接删除了resolv.conf: rm /etc/resolv.conf

再生成写入:

echo "nameserver 192.168.1.1" > /etc/resolv.conf
echo "nameserver 114.114.115.115" > /etc/resolv.conf

然后再设置只读:sudo chattr -f +i /etc/resolv.conf

再次重启,DNS没有丢失。

如果要在windows中直接修改,设置为属性可写:sudo chattr -i /etc/resolv.conf

不知道为什么必须要先删除,才再建resolv.conf,才可以修改成功。。。

【end】

解决WSL因盘符变更,系统找不到指定的路径

新加了一块硬盘(F), 将WSL存放的老盘(L)数据拷入新硬盘(F)

之后因盘符变更, 启动wsl时提示如下:

无法将磁盘“L:\WSL\Debian\ext4.vhdx”附加到 WSL2: 系统找不到指定的路径。
错误代码: Wsl/Service/CreateInstance/MountVhd/HCS/ERROR_PATH_NOT_FOUND

[已退出进程,代码为 4294967295 (0xffffffff)]

问题是去哪里修改? 虚拟机调用镜像的设置保存在哪里?

找了篇微软文档参考: 如何查找 Linux 发行版的 .vhdx 文件和磁盘路径 https://learn.microsoft.com/zh-cn/windows/wsl/disk-space#how-to-locate-the-vhdx-file-and-disk-path-for-your-linux-distribution

.vhdx 文件和磁盘路径, 存储在注册表的以下地址中:

HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss

Lxss 下 类似 { guid } 的条目就是子系统条目, 有几个子系统会有几条。(我的有3个子系统 )

条目下数值项 “BasePath”,就是文件地址目录,直接修改盘符路径,重启wsl就可以了!

其他问题

如果修改注册表,wsl启动后,wsl提示:

<3>WSL (xxx) ERROR: UtilTranslatePathList:2852: Failed to translate” (之后跟着一个windows文件路径)

如我的提示:

<3>WSL (461) ERROR: UtilTranslatePathList:2852: Failed to translate L:\PHP\php-8.1.23-nts-Win32-vs16-x64

说明后面这个路径,是被设置在系统环境变量中,还是有由于盘符变动造成的问题。把系统环境变量的路径值修改了,就好了!

【end】

对sqlite已有数据,添加多列的唯一性约束

以一个数据表为例, 创建sql如下:

CREATE TABLE http_table (
    id       INTEGER PRIMARY KEY AUTOINCREMENT,
    Host     TEXT    DEFAULT "",
    IP       TEXT    DEFAULT "",
    Port     INTEGER DEFAULT (0),
    Info     TEXT    DEFAULT "",
    Uptime   TEXT    DEFAULT (datetime('now', 'localtime') ) 
);

当前该表已有不少数据存在,只有id为自增长主键,未创建约束。

之前创建数据过程,先查询(1),不存在新插入一条(2),存在更新该条记录(3):

(1)先查询

SELECT id FROM http_table WHERE Host = 'dns9.quad9.net' AND IP = '9.9.9.9' AND Port = '80' order by Uptime desc LIMIT 1;

(这里,可能是多条,只返回一条)

(2)不存在新插入一条

INSERT INTO http_table (Host, IP, Port, Info) VALUES ('dns9.quad9.net', '9.9.9.9', '80', 'new_info');

(3)存在更新该条记录

UPDATE http_table SET Info = 'new_info' WHERE Host = 'dns9.quad9.net' AND IP = '9.9.9.9' AND Port = '80';

本身这个更新并不严谨,没有指定唯一id条件,可能会更新多条,但这不是要解决的问题。

问题在于在大批量的数据插入时,每次都要查询,再判断是插入还更新,速度很慢,现需要一条sql一次完成查询,并完成插入或更新。

尝试修改:在 SQLite 中,可以使用 INSERT INTO...ON CONFLICT 子句配合 DO UPDATE 动作,实现根据特定条件存在则更新记录,不存在则插入新记录的功能。

INSERT INTO http_table (Host, IP, Port, Info)
VALUES ('dns9.quad9.net', '9.9.9.9', '80', 'new_info')
ON CONFLICT(Host, IP, Port) DO UPDATE SET
    Info=excluded.Info;
解释:

INSERT INTO http_table (Host, IP, Port, Info) VALUES (...) 尝试插入一条新记录。


ON CONFLICT(Host, IP, Port) 指定当 Host, IP, Port 这三个字段的组合发生冲突时(即已存在这样的记录时)应采取的动作。


DO UPDATE SET Info=excluded.Info 指定当冲突发生时,更新已有记录的 Info 字段。excluded 是一个特殊的表别名,它引用尝试插入但因冲突而未能插入的值。


这样,如果记录已存在,Info 字段将被更新为新的值;如果记录不存在,则将插入新的记录

首先,你需要确保你的表有一个唯一约束(UNIQUE CONSTRAINT)来检测冲突。在这个例子中,需要设定 HostIP, 和 Port 的组合是唯一的,UNIQUE(Host, IP, Port)否则会报错。

CREATE TABLE http_table(
    Host TEXT,
    IP TEXT,
    Port TEXT,
    Info TEXT,
    UNIQUE(Host, IP, Port)
);

由于之前未添加唯一约束,执行INSERT INTO http_table ... ON CONFLICT(Host, IP, Port) DO UPDATE ... 会如下报错:

执行 SQL 查询时发生错误:ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint

解决方法建立约束前,清洗现有数据

在建立唯一性约束之前,如果现有数据中存在违反该约束的重复记录,你需要进行数据清洗。数据清洗的过程通常包括识别重复记录、决定如何处理这些记录(如删除、合并或修改),以及执行相应的操作。以下是一些步骤和建议,帮助你清洗现有数据以便能够添加唯一性约束:

识别重复记录

使用SQL查询来找出在HostIPPort列上有重复组合的记录。可以使用GROUP BYHAVING子句来识别这些重复项。以下查询将找出所有重复的HostIPPort组合以及它们出现的次数:

SELECT Host, IP, Port, COUNT(*) as count FROM http_table GROUP BY Host, IP, Port HAVING COUNT(*) > 1;

决定如何处理重复记录

  • 删除重复项:如果重复记录没有保留价值,你可以选择删除它们。确保在删除之前备份数据,以防万一。
  • 合并记录:如果重复记录包含有用的信息,你可能想要合并它们。这通常涉及更新一条记录以包含所有相关信息,并删除其他重复记录。
  • 修改记录:在某些情况下,你可能能够通过修改重复记录中的一个或多个字段来消除重复。

执行数据清洗操作

根据你的决定,编写SQL语句来执行删除、合并或修改操作。

如果你决定删除重复的记录,并且只保留每个组合的第一条记录,你可以使用以下查询(请注意,这个查询假设你有一个唯一标识每条记录的id字段):

DELETE FROM http_table WHERE id NOT IN ( SELECT MIN(id) FROM http_table GROUP BY Host, IP, Port );

这个查询会删除每个HostIPPort组合中除id最小的一条记录之外的所有记录。

验证数据清洗结果

在执行数据清洗操作后,再次运行识别重复记录的查询,确保所有重复项都已被处理。

检查数据以确保没有其他问题,如数据丢失或不一致。

添加唯一性约束

确认数据已经清洗干净,没有违反唯一性约束的重复记录,可以安全地添加唯一性约束了。使用ALTER TABLE语句来添加约束:

ALTER TABLE http_table 
ADD CONSTRAINT unique_host_ip_port UNIQUE(Host, IP, Port);

这时又发生错误!

执行 SQL 查询时发生错误:near "CONSTRAINT": syntax error

在SQLite中,ALTER TABLE 语句用于修改表的结构,比如添加列、重命名表、添加约束等。但是,SQLite 在使用 ALTER TABLE 添加约束时有一些限制,特别是关于唯一性约束(UNIQUE CONSTRAINT)。

在SQLite中,如果你想要给一个已经存在的表添加一个新的唯一性约束,并且这个约束涉及到多个列,你不能直接在 ALTER TABLE 语句中使用 ADD CONSTRAINT 语法。SQLite不支持这种直接添加多列唯一性约束的方式。

相反,你需要采取一种间接的方法来实现这一点。以下是一种可能的解决方案:

  1. 创建一个新表:这个新表应该包含与原始表相同的列,并且包含你想要添加的唯一性约束。
  2. 复制数据:将原始表中的数据复制到新表中,同时确保没有违反唯一性约束的数据被插入。
  3. 删除原始表(可选):如果数据复制成功,并且你确定新表包含了所有需要的数据,你可以删除原始表。
  4. 重命名新表:将新表重命名为原始表的名称。

务必注意:备份数据

在进行任何数据修改或结构更改之前,始终确保你有最新的数据备份。这是防止数据丢失或损坏的重要步骤。

请记住,数据清洗是一个可能对数据产生重大影响的操作。在执行任何删除或修改操作之前,务必仔细考虑并备份你的数据。

最终执行的SQL语句是:

--修改原更新语句,使用 INSERT INTO ... ON CONFLICT ... DO UPDATE 方式,一次查询执行插入或删除。

INSERT INTO http_table (Host, IP, Port, Title, Url, Info, Location, Method, Code, Tag, Server) 
VALUES ('Host', 'IP', 'Port', 'Title', 'Url', 'Info', 'Location', 'Method', 'Code', 'Tag', 'Server') 
ON CONFLICT (Host, IP, Port) DO UPDATE SET Info = excluded.Info;

-- 添加唯一约束,操作步骤:

-- 首先备份原始的 .db 文件

-- 识别重复记录
SELECT Host, IP, Port, COUNT( * ) AS count
  FROM http_table
 GROUP BY Host, IP, Port
HAVING COUNT( * ) > 1;

-- 删除重复记录
DELETE FROM http_table
      WHERE id NOT IN (
    SELECT MIN(id) 
      FROM http_table
     GROUP BY Host, IP, Port
);

-- 创建包含唯一性约束的新表
CREATE TABLE new_http_table (
    id       INTEGER PRIMARY KEY AUTOINCREMENT,
    Host     TEXT    DEFAULT "",
    IP       TEXT    DEFAULT "",
    Port     INTEGER DEFAULT (0),
    Title    TEXT    DEFAULT "",
    Url      TEXT    DEFAULT "",
    Info     TEXT    DEFAULT "",
    Location TEXT    DEFAULT "",
    Method   TEXT    DEFAULT "",
    Code     INTEGER DEFAULT "",
    Tag      TEXT    DEFAULT "",
    Server   TEXT    DEFAULT "",
    Uptime   TEXT    DEFAULT (datetime('now', 'localtime') ) ,
    UNIQUE(Host, IP, Port)
);

-- 复制数据到新表(确保没有重复项)
INSERT INTO new_http_table (id, Host, IP, Port, Title, Url, Info, Location, Method, Code, Tag, Server, Uptime)
	SELECT id, Host, IP, Port, Title, Url, Info, Location, Method, '', Tag, Server, Uptime
	FROM http_table
	WHERE (Host, IP, Port) IN (SELECT Host, IP, Port
								FROM http_table
								GROUP BY Host, IP, Port
								HAVING COUNT( * ) = 1);

-- 查询对比新旧两个表数据总数
SELECT (SELECT COUNT( * ) 
          FROM http_table) AS old_count, (SELECT COUNT( * ) 
                                                    FROM new_http_table) AS new_count;

-- (可选)删除原始表
--DROP TABLE http_table;

-- 重命名原始表的名称
ALTER TABLE http_table RENAME TO back_http_table;

-- 重命名新表为原始表的名称
ALTER TABLE new_http_table RENAME TO http_table;

-- (可选)删除备份表
--DROP TABLE back_http_table;

--释放空闲占用(类似收缩数据库)
VACUUM;

--检查数据库完整性
PRAGMA integrity_check;

--数据处理全部完成

使用 hping 时报错的处理

Linux中使用Nmap , hping, ping 提示以下错误时

PHP: [main]	can't	open	raw	socket

Bash: Couldn’t open a raw socket. Error: Permission denied (13)

非root进程调用带有网络原始套接字(raw socket) 的相关命令时,会报错,需要使用getcap 设置 capabilities

(具体参考:https://cloud.tencent.com/developer/article/1539041)

# 以 ping 为例,先 which 查看命令地址
which ping

# 如无返回就是没有设置cap
getcap /usr/bin/ping

# 设置cap
sudo setcap cap_net_raw+ep /usr/bin/ping

# 再次getcap 已有设置值
getcap /usr/bin/ping
#/usr/bin/ping cap_net_raw=ep

setcap 之后问题就解决了。

Linux下date的默认输出格式

在两个debian主机下 使用date命令,显示的格式不一样,分为两种(A/B):

  • A:Sat Sep 14 10:15:03 PM CST 2024
  • B:Sat Sep 14 22:15:15 CST 2024

以晚上十点一刻为例,A 的10:15是12小时制,带有PM, B的22:15是24小时制,没有PM。

所以问题是, 如何设置12小时制 为 24小时制? 继续尝试几种涉及时间设置的命令。

使用 timedatectl查看 : (题外话:该命令在wsl,windows子系统中无效

timedatectl status
 timedatectl status
Local time: Sat 2024-09-14 21:56:01 CST
Universal time: Sat 2024-09-14 13:56:01 UTC
RTC time: Sat 2024-09-14 13:55:53
Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: no
NTP service: n/a
RTC in local TZ: no
timedatectl status
Local time: Sat 2024-09-14 21:56:07 CST
Universal time: Sat 2024-09-14 13:56:07 UTC
RTC time: n/a
Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: no
NTP service: n/a
RTC in local TZ: no

“Local time” , “Universal time”, “Time zone” 时间都是正确,并一致的。时钟同步,NTP服务也都并未使用。RTC time一个有时间,一个为空,应该是虚拟化不同的问题。看来timedatectl的信息并不显示时间是否12/24制式。

locale 查看本地设置:

locale -a
C
C.utf8
en_US.utf8
POSIX
C
C.utf8
POSIX
en_US.utf8

都是 “en_US.utf8″,只是 POSIX顺序不用,也无关。

使用 dpkg-reconfigure tzdata 重新设置时区:

dpkg-reconfigure tzdata

之后再看date,结果证明也与时间12/24制式无关。

使用 date –debug,会显示输出格式:

date --debug
date --debug
date: output format: ‘%a %b %e %r %Z %Y’
Sat Sep 14 10:15:03 PM CST 2024
date --debug
date: output format: '%a %b %e %H:%M:%S %Z %Y'
Sat Sep 14 22:14:50 CST 2024

看到不同了把? date 输出不同时间进制,与设置时间的命令无关。只有输出个格式的不同。那么这个格式是什么决定的?在哪里定义的?

之后看到这篇文章:https://linuxopsys.com/change-default-date-format-in-linux

原来 date 命令的默认日期格式。是根据 locale 中的 LC_TIME 参数,根据参数名,使用关联的设置文件。

例如,运行 locale 命令并查找 LC_TIME 变量。

locale

如图,看到 LC_TIME = “en_US.UTF-8“,这是美国常用的日期格式。

第 2 步:找到与 Locale 关联的文件
locale 文件通常位于 /usr/share/i18n/locales/ 中。您将找到一个名称与您的 LC_TIME locale 设置相对应的文件,如果 LC_TIME 区域设置是 en_US, 对应文件为/usr/share/i18n/locales/en_US


查看 Locale 文件,可以看到其中的 date_fmt 之后的定义 与 date –debug 显示的输出格式一样,

//12小时
"%a %b %e %r %Z %Y"

//24小时
"%a %b %e %H:%M:%S %Z %Y"
~ # date +"%a %b %e %r %Z %Y"
Sun Sep 15 12:16:14 AM CST 2024

~ # date +"%a %b %e %H:%M:%S %Z %Y"
Sun Sep 15 00:17:31 CST 2024

使用 date + “输出格式” 测试, 由此我们发现了问题根源。但是什么,两个“LC_TIME ” 都是 “en_US”,格式会不同呢?答案在 en_US 文件的注释中:

% Appropriate date and time representation for date(1). This is
% different from d_t_fmt for historical reasons and has been different
% since 2000 when date_fmt was added as a GNU extension. At the end
% of 2018 it was adjusted to use 12H time (bug 24046) instead of 24H.

也就是说,一个系统是2018年前安装,或设置生成的 Locale 文件,一个是在2018年之后

结尾

如此,明白了问题根源,就可以通过修改 locale 文件的date_fmt 或 重新设置 LC_TIME 环境变量,重新生成区域设置文件以应用更改。

locale-gen
update-locale

或者 用更简单的方法:

export LC_TIME=POSIX

直接设置 LC_TIME=POSIX,可以快速统一多台,不同本地语言设置的时间格式。

【END】