Vim / 鼠标无法选中复制

问题出现在 Manjaro 上,vim 打开文件,鼠标选中后仍然是箭头形状,无法复制。 临时解决 按 :,再输入以下指令回车即可, set mouse-=a 永久解决 在 /etc/vimrc 中配置 set mouse-=a,不生效。 执行 vim -V 打开文件,观察读取配置文件的顺序, [vksir-swiftsf51451 ~]# vim -V 1.py 2>&1 | grep 继续 在 /etc/vimrc 中继续 在 /usr/share/vim/vim91/defaults.vim 中继续 在 /usr/share/vim/vim91/defaults.vim 中继续 在 /usr/share/vim/vim91/defaults.vim 中继续 在 /usr/share/vim/vim91/syntax/syncolor.vim 中继续 在 /usr/share/vim/vim91/syntax/synload.vim 中继续 在 /usr/share/vim/vim91/syntax/syntax.vim 中继续 在 /usr/share/vim/vim91/defaults.vim 中继续 请按 ENTER 或其它命令继续 最后读取了 /usr/share/vim/vim91/defaults.vim,在该文件末尾加上 set mouse-=a 生效。 写在后面 如此可知 /etc/vimrc 中配置不生效的原因可能是被后续配置覆盖了,在 /usr/share/vim/vim91/defaults.vim 中配置也不太好,可能升级 vim 该配置就丢失了。 但,我也懒得花时间研究了。

五月 12, 2025  |  69 字  |  总阅读

WSL / 默认使用 root 用户

打开 PowerShell,以 root 用户进入 wsl, wsl -u root 再修改 root 密码, passwd 修改 /etc/wsl.conf 文件,添加, [user] default=root 保存退出。 再退出到 PowerShell,重启 wsl, wsl -t ubuntu 重新进入 wsl,即发现默认用户即 root。

五月 12, 2025  |  27 字  |  总阅读

优质工具 / Voicemeter 音轨配置

基本用法 Voicemeter 功能非常强大,我仅使用其冰山一角。 录制 跟着绿色箭头: 点击 Stereo Input 1,选择硬件麦克风,麦克风录音从这里输入 Voicemeter 点亮 B3,麦克风录音从 B3 音轨走 看最右侧 B3,麦克风录音在这里调音,最终从 Voicemeeter Out B3 设备送入操作系统。 播放 跟着红色箭头: 在操作系统中选择 Voicemeeter VAIO3 Input 设备,媒体声音(比如游戏声音)从该设备送入 Voicemeter 点亮 A1,媒体声音从 A1音轨走 点击 A1,选择硬件扬声器,放出声音 理一下: 录制:硬件麦克风 > B3 > 虚拟麦克风 Voicemeeter Out B3 > 操作系统录制 播放:操作系统播放 > 虚拟扬声器 Voicemeeter VAIO3 Input > A1 > 硬件耳机 很清晰了。 使用场景 1 快捷在耳机和扬声器间切换,或者想将声音在多个播放设备中同时播放 A1 选择硬件耳机,A2 选择硬件扬声器 Voicemeeter VAIO3 Input 虚拟扬声器同时勾选 A1、A2 轨道 如此,媒体声音将从虚拟扬声器进入Voicemeter,复制两份,同时走 A1、A2 硬件,这时只有有选择的将其中某个扬声器禁音即可。 ...

五月 12, 2025  |  105 字  |  总阅读

搭建家庭服务器

时隔多年,又开始折腾服务器了。这次想在家里摆个服务器,就用淘汰下来的笔记本,花了两三天时间,遂记录一下。 万事始于公网 IP 还得是公网 IP。但令我没想到的是,打电话给电信,刚开口说想申请个公网 IP,人家就给我下了订单,我提前想好的理由一句没说。没半个小时一个电话过来,让我检查下 OK 不 OK——太效率了! 电信,赞! 路由器拨号 光有公网 IP 还不行,还得配置光猫桥接,让路由器拨号。 这一点要吐槽电信了,打电话第一个客户说不支持桥接,问原因她也不说,就说不支持——骗鬼呢。 打了第二个电话,换了个客户,还说不支持,但说了原因,大意是改桥接问题很多,很多年前就取消了这项服务。 我不是没想过自己去改,但是我家那个光猫型号老,后台(80 端口)以及管理后台(8080 端口)都没有改桥接的地方,我不知道是不是要超级管理员账号才能改,但超级管理员密码我也拿不到,电信客户也不给,问就是“没有”、“不知道”。 最后还是电信维修师傅帮忙改的桥接,也是光速改,很快啊。赞! 清理笔记本数据 笔记本虽然之前应该备份过数据,但以防万一,还是又备份了一遍。 两台笔记本摆一起,连上 WIFI,开启 Windows 文件共享,选好想备份的文件等它传好就完事了。 选择 Linux 发行版 虽然这笔记本是当服务器来使,365 天不停机,但还是得给它安排桌面版 Linux 系统,毕竟家里爸妈放电视剧、电影还得靠它。 选 Linux 发行版时给我纠结了一下,Linux Mint 和 Manjaro 其实都很好。 Linux Mint 优势在于它是 Ubuntu 系的,很多开源软件基本第一个要支持的就是 Ubuntu,特别是那种一键式安装脚本,对 OS 依赖较强,支持的也一般包含 Ubuntu。装 Ubuntu 绝对省心。 Manjaro 优势就在于有 AUR,基本什么软件都能很方便的安装,虽然每次安装都要编译就是了。 最终还是选了 Manjaro。 安装 Linux 系统 制作启动盘一开始用的软碟通,最后发现没法启动。后面又换了 Refus,使用 DD 模式刻录,才成功。 Linux 系统配置 代理配置 代理是个大问题。 ...

九月 21, 2024  |  625 字  |  总阅读

【Go】使用 logrus 日志库

相比起 zap 日志库,logrus 性能会低一些,但在很多场景,并不需要太高性能,而 logrus 库的自定义能力较为强大,可以任意定义日志格式,较为方便。 package main import ( "context" "fmt" "github.com/sirupsen/logrus" "io" "os" "path/filepath" "runtime" "strings" ) const logPath = "./log.txt" var log = New() func New() *Logger { return &Logger{logger: logrus.New()} } type Logger struct { ctx context.Context logger *logrus.Logger } func (l *Logger) SetFormatter(formatter logrus.Formatter) { l.logger.SetFormatter(formatter) } func (l *Logger) SetOutput(output io.Writer) { l.logger.SetOutput(output) } func (l *Logger) SetLevel(level logrus.Level) { l.logger.SetLevel(level) } func (l *Logger) WithContext(ctx context.Context) *Logger { return &Logger{ctx: ctx, logger: l.logger} } func (l *Logger) Debug(format string, args ...any) { l.log(logrus.DebugLevel, format, args...) } func (l *Logger) Info(format string, args ...any) { l.log(logrus.InfoLevel, format, args...) } func (l *Logger) Warn(format string, args ...any) { l.log(logrus.WarnLevel, format, args...) } func (l *Logger) Error(format string, args ...any) { l.log(logrus.ErrorLevel, format, args...) } func (l *Logger) Fatal(format string, args ...any) { l.log(logrus.FatalLevel, format, args...) } func (l *Logger) log(level logrus.Level, format string, args ...any) { format = fmt.Sprintf("[%s] %s", GetCallerShort(3), format) l.logger.WithContext(l.ctx).Logf(level, format, args...) } type Formatter struct{} func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) { pid := os.Getpid() time := entry.Time.Format("2006-01-02T15:04:05.000000") level := strings.ToUpper(entry.Level.String()) traceId := GetTraceIdFromCtx(entry.Context) content := fmt.Sprintf("%s %s %d %s %s\n", time, level, pid, traceId, entry.Message) return []byte(content), nil } // GetCallerShort 获取调用点文件名 + 行号 func GetCallerShort(skip int) string { _, file, line, ok := runtime.Caller(skip + 1) if !ok { return "" } _, file = filepath.Split(file) return fmt.Sprintf("%s:%d", file, line) } // CtxSetTraceId 在 ctx 中设置 trace id func CtxSetTraceId(ctx context.Context) context.Context { return context.WithValue(ctx, "trace_id", "id1") } // GetTraceIdFromCtx 打印日志时,从 ctx 中获取 trace id 打印 func GetTraceIdFromCtx(ctx context.Context) string { if ctx == nil { return "-" } val := ctx.Value("trace_id") if traceId, ok := val.(string); ok { return fmt.Sprintf("trace-%s", traceId) } else { return "-" } } func init() { f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o640) if err != nil { panic(err) } log.SetFormatter(&Formatter{}) log.SetOutput(io.MultiWriter(os.Stdout, f)) log.SetLevel(logrus.DebugLevel) } func main() { ctx := context.Background() ctx = CtxSetTraceId(ctx) log.WithContext(ctx).Info("main failed: %s", "detail") }

三月 13, 2024  |  326 字  |  总阅读

【优质工具】Scapy 发包工具

Python 的 scapy 库是一个非常优秀的发包工具,可以灵活构造各种报文。 import socket import struct from scapy.all import * from scapy.layers.inet import * from scapy.layers.inet6 import * RS = '120.55.68.91' TIMEOUT = 0.5 IPv4 ICMP 报文 pkt = IP(dst=RS)/ICMP() pkt.show() print(hexdump(pkt)) sr1(pkt, timeout=TIMEOUT) ###[ IP ]### version = 4 ihl = None tos = 0x0 len = None id = 1 flags = frag = 0 ttl = 64 proto = icmp chksum = None src = 192.168.1.112 dst = 120.55.68.91 \options \ ###[ ICMP ]### type = echo-request code = 0 chksum = None id = 0x0 seq = 0x0 unused = '' 0000 45 00 00 1C 00 01 00 00 40 01 FC 35 C0 A8 01 70 E.......@..5...p 0010 78 37 44 5B 08 00 F7 FF 00 00 00 00 x7D[........ None Begin emission: Finished sending 1 packets. Received 2 packets, got 1 answers, remaining 0 packets IPv4 UDP 报文 pkt = IP(dst=RS)/UDP(sport=3000, dport=82) pkt.show() print(hexdump(pkt)) sr1(pkt, timeout=TIMEOUT) ###[ IP ]### version = 4 ihl = None tos = 0x0 len = None id = 1 flags = frag = 0 ttl = 64 proto = udp chksum = None src = 192.168.1.112 dst = 120.55.68.91 \options \ ###[ UDP ]### sport = 3000 dport = 82 len = None chksum = None 0000 45 00 00 1C 00 01 00 00 40 11 FC 25 C0 A8 01 70 E.......@..%...p 0010 78 37 44 5B 0B B8 00 52 00 08 75 29 x7D[...R..u) None Begin emission: Finished sending 1 packets. Received 9 packets, got 1 answers, remaining 0 packets 携带 Option 的 IPv4 UDP 报文 opt = IPOption(option=0x1f, length=8, value=0) pkt = IP(dst=RS, options=opt)/UDP(sport=3000, dport=82) pkt.show() print(hexdump(pkt)) sr1(pkt, timeout=TIMEOUT) ###[ IP ]### version = 4 ihl = None tos = 0x0 len = None id = 1 flags = frag = 0 ttl = 64 proto = udp chksum = None src = 192.168.1.112 dst = 120.55.68.91 \options \ |###[ IP Option ]### | copy_flag = 0 | optclass = control | option = 31 | length = 8 | value = 0 ###[ UDP ]### sport = 3000 dport = 82 len = None chksum = None 0000 46 00 00 20 00 01 00 00 40 11 DC 19 C0 A8 01 70 F.. ....@......p 0010 78 37 44 5B 1F 08 00 00 0B B8 00 52 00 08 75 29 x7D[.......R..u) None Begin emission: Finished sending 1 packets. Received 7 packets, got 0 answers, remaining 1 packets 携带 IPv6 Destination Extension Header 的 UDP 报文 opt = HBHOptUnknown(otype=0x1f, optlen=6, optdata=0) pkt = IPv6(dst=RSV6)/IPv6ExtHdrDestOpt(options=opt)/UDP(sport=3000, dport=82) pkt.show2() print(hexdump(pkt)) sr1(pkt, timeout=TIMEOUT) ###[ IPv6 ]### version = 6 tc = 0 fl = 0 plen = 16 nh = Destination Option Header hlim = 64 src = :: dst = fe02::2 ###[ IPv6 Extension Header - Destination Options Header ]### nh = UDP len = 0 autopad = On \options \ |###[ Scapy6 Unknown Option ]### | otype = 31 [00: skip, 0: Don't change en-route] | optlen = 6 | optdata = '\x01\x02\x00\x00' ###[ UDP ]### sport = 3000 dport = 82 len = 8 chksum = 0xf5cf

四月 26, 2023  |  519 字  |  总阅读

八方旅人 2

旅途终了,总感觉是要写点什么的。 八方旅人一代给我的感觉非常好,一个史诗、绚烂、浪漫的旅途。旅途——总是浪漫的。玩完之后的那种寂寥,感觉那八个人离我而去了,是非常不舍的。 不知是二代退步了,还是我本人心态有所变化,那种游戏通关后的寂寥感,是没了。 现在是 2023年,毕业后三年,初玩八方旅人一代大抵在我上大学那会儿,貌似时间间隔也不久。 从哪里聊起呢?既然是八方旅人,就一个个聊来好了。 ——会不会太长? 盗贼-斯洛妮 Throne——惨。 始于青梅竹马相杀,再杀蛇父蛇母,再杀父。追寻自由的旅途,一路鲜血。 本以为蛇父就是亲生父亲,虽然有点老套,但我希望 Throne 能好——但可惜不是。蛇父和亲生母亲生下孩子,生后亲生母亲和黑蛇跑了,生下了 Throne,并且还把蛇父的孩子杀了。所以亲生母亲不是什么好货,亲生父亲,同样不是。 Throne,旅途终点,大家都有各自的目标,唯独她,拿到了自由,想去无人探索过的新世界。 望安。 因这代都没什么清图的技能,外加上先驱等效加一点能量,清图基本靠盗公子的钩爪。还是挺高效的。 Throne 的武器大师还是飒的! 学者-奥兹巴尔多 受一代影响,旅途起点盗贼,同样受一代影响,第二喜欢的就是学者了。 不过二代学者是个大叔,不像一代是经典木讷日漫男主——智商高,情商说不好,指不定人家装的呢~ 中等伤害的启动较之一代更慢——因为要上大魔法化,导致平时刷图基本不用。 峰值启动会快一点——不需要自己给自己上魔法暴击,不需要神官大。打隐藏 Boss 的时候,叠 Buff 相较一代简单太多了。 这就是我的解! 大叔还是惨的,只是因其性格古板,显得不那么惨。(已经够惨了!) 剑士-光 主角模板~ Hikari,妥妥的版本之子,主角模板。 较之一代的欧叔,吸睛太多——又帅伤害又高,可以学各式各样的技能,破盾打伤害都是好手,魔武双修,全能人物。 欧叔只有巅峰的豪武匠,可以压过武器大师一头——武器大师放光身上,是真难看。 而且光本身背景也是庶出王子,三年之期,且王朝建筑还很有中国风,直接爱了。 狮子狩猎! 猎人-欧修缇 唯一指名认证团宠! 基本是对应了一代里的特蕾莎了,可爱程度不相上下。 并且这代猎人过于强势,批量唆使,最高一回合全体破 12 盾,离谱得很。并且十星宠物的伤害也非常高——当然只是中期。 当然,因其武器没法得到充分使用,这决定了她只能做个破盾工具人。 有事没事就是“提拉库~~”。 耳朵呆耸的样子也非常可爱!(小狼谁管你!) ...

三月 14, 2023  |  86 字  |  总阅读

ARP 协议

ARP 格式 struct arphdr { unsigned short int ar_hrd; /* Format of hardware address. */ unsigned short int ar_pro; /* Format of protocol address. */ unsigned char ar_hln; /* Length of hardware address. */ unsigned char ar_pln; /* Length of protocol address. */ unsigned short int ar_op; /* ARP opcode (command). */ }; struct ether_arp { struct arphdr ea_hdr; /* fixed-size header */ uint8_t arp_sha[ETH_ALEN]; /* sender hardware address */ uint8_t arp_spa[4]; /* sender protocol address */ uint8_t arp_tha[ETH_ALEN]; /* target hardware address */ uint8_t arp_tpa[4]; /* target protocol address */ }; Ethernet II 格式 typedef unsigned short __u16; typedef __u16 __bitwise __be16; struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ __be16 h_proto; /* packet type ID field */ } __attribute__((packed)); C 收发 ARP 报文 发 #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <linux/if_ether.h> #include <netinet/if_ether.h> #include <linux/if_packet.h> #include <net/if.h> #include <string.h> #include <net/if_arp.h> #include <arpa/inet.h> #include <unistd.h> int main() { // Create raw socket int socket_fd = socket(AF_PACKET, SOCK_RAW, 0); if (socket_fd < 0) { perror("create socket failed"); exit(1); } // Bind interface and get local mac struct sockaddr_ll local; local.sll_family = AF_PACKET; local.sll_ifindex = (int) if_nametoindex("eth0"); socklen_t local_len = sizeof local; if (bind(socket_fd, (struct sockaddr *) &local, local_len) < 0) { perror("bind dev failed"); exit(1); } getsockname(socket_fd, (struct sockaddr *) &local, &local_len); // Create packet unsigned char buf[256]; struct ethhdr *eh = (struct ethhdr *) buf; struct ether_arp *arp = (struct ether_arp *) (eh + 1); size_t size = (unsigned char *) (struct ether_arp *) (arp + 1) - buf; // Ethernet II header memset(eh->h_dest, 255, ETH_ALEN); memcpy(eh->h_source, local.sll_addr, ETH_ALEN); eh->h_proto = htons(ETH_P_ARP); // Arp header arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); arp->ea_hdr.ar_pro = htons(ETH_P_IP); arp->ea_hdr.ar_hln = ETH_ALEN; arp->ea_hdr.ar_pln = 4; arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); // Sender addr memcpy(arp->arp_sha, local.sll_addr, ETH_ALEN); struct in_addr src_ip; inet_aton("172.22.211.129", &src_ip); memcpy(arp->arp_spa, &src_ip.s_addr, 4); // Target addr memset(arp->arp_tha, 255, ETH_ALEN); struct in_addr dst_ip; inet_aton("172.22.208.1", &dst_ip); memcpy(arp->arp_tpa, &dst_ip.s_addr, 4); while (1) { if (send(socket_fd, buf, size, 0) < 0) { perror("send arp failed"); exit(1); } printf("send arp success\n"); sleep(1); } } 存在 char * 和 struct 的相互转化,因结构体个变量地址连续且排列顺序固定,所以可以这么做。——如此编程精巧但易出错,只能说不愧是 C! ...

三月 4, 2023  |  662 字  |  总阅读

【Go】Gin 自定义参数校验和错误信息

Gin 使用 validator 库做数据校验,如下可以实现自定义校验、及自定义校验错误信息。 其中,自定义错误信息无法用于多层嵌套结构体,可能可以通过反射做到,但感觉在性能上很捉急。 代码 package main import ( "errors" "fmt" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" "net/http" "reflect" "strings" ) type Body struct { Name string `json:"name" binding:"oneof=vk vksir" err:"one of vk,vksir"` Age int `json:"age" binding:"BodyAgeValidate" err:"only 18"` } func BodyAgeValidate(f validator.FieldLevel) bool { value := f.Field().Int() if value != 18 { return false } return true } func GetValidateErr(obj any, rawErr error) error { validationErrs, ok := rawErr.(validator.ValidationErrors) if !ok { return rawErr } var errString []string for _, validationErr := range validationErrs { field, ok := reflect.TypeOf(obj).FieldByName(validationErr.Field()) if ok { if e := field.Tag.Get("err"); e != "" { errString = append(errString, fmt.Sprintf("%s: %s", validationErr.Namespace(), e)) continue } } errString = append(errString, validationErr.Error()) } return errors.New(strings.Join(errString, "\n")) } func ping(c *gin.Context) { b := Body{} if err := c.ShouldBindJSON(&b); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": GetValidateErr(b, err).Error()}) return } c.JSON(http.StatusOK, gin.H{}) } func main() { e := gin.Default() if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterValidation("BodyAgeValidate", BodyAgeValidate) } e.GET("/ping", ping) e.Run("127.0.0.1:8080") } 运行 curl --location --request GET 'http://127.0.0.1:8080/ping' \ --header 'Content-Type: application/json' \ --data-raw '{ "name": "v", "age": 19 }' { "detail": "Body.Name: one of vk,vksir\nBody.Age: only 18" } 参考文档: ...

二月 8, 2023  |  203 字  |  总阅读

【Go】使用 zap 日志库

代码 loging\logging.go package logging import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) const logPath = "./log.txt" func SugaredLogger() *zap.SugaredLogger { return zap.S() } func init() { encoder := getEncoder() writeSyncer := getWriteSyncer() core := zapcore.NewCore(encoder, writeSyncer, zap.DebugLevel) logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)) zap.ReplaceGlobals(logger) } func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder return zapcore.NewConsoleEncoder(encoderConfig) } func getWriteSyncer() zapcore.WriteSyncer { f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { panic(err) } return zapcore.NewMultiWriteSyncer(zapcore.AddSync(f), zapcore.AddSync(os.Stdout)) } 使用 main.go package main import ( "GoCode/logging" ) var log = logging.SugaredLogger() func main() { log.Info("Hello zap") } 2023-02-06T01:26:29.514+0800 INFO GoCode/main.go:11 Hello zap

二月 8, 2023  |  102 字  |  总阅读