vscode为不同项目配置不同的go版本

前言 之前的Go多版本共存解决了多个go版本的问题,但不同项目使用不同的go版本,需要手动切换,而且容易出些小问题。如果能实现IDE在不同项目间切换的同时自动切换到对应的go版本,那就完美了。 解决方案 如果使用的是vscode,只需要配置一下即可。 手动下载所需版本的go 用vscode打开项目,项目根目录下创建.vscode目录,已有可忽略该步 在.vscode目录下创建settings.json文件,已有可忽略该步 在settings.json中写入以下内容: 1 2 3 { "go.goroot": "/指定go版本的安装路径/如go1.26.1" } 注意:路径需要是绝对路径,而且要和go.mod里的版本一致! 重启vscode

2026-03-09 17:22:44 · 1 分钟 · 慢步道人

使用Docker+vscode搭建离线的go开发调试环境

背景 受项目及信创环境影响,导致debug极不方便,主要限制如下: 无法连接互联网 客户端只能使用win7或国产系统,受使用习惯及生态影响,实际只能使用win7 无法debug终究不是个事,还是需要有一个解决方案的。 解决过程 方案1 首先想到的方案便是在某一客户端上安装vscode+go搭建一个简易的开发环境。 vscode支持win7的最后一个版本是1.70.3,搞定这个安装包还是不难的;但go支持win7的最后一个版本是1.20.x,为了使用一些新的特性,至少得是1.23.x以上。 方案2 方案1行不通,那就只能打服务器的主意了。在win7上装vscode及相关插件,在linux服务器上装go,进行远程调试。 评估过程中发现,既要搞win7的插件,又要搞linux的服务,还要处理go的依赖……麻烦! 方案3 既然服务器是linux系统,应用部署使用的是docker,而且vscode是有web版的(叫code server),那么使用docker来搞定这个开发环境应该问题不大的,而且这个镜像还可以到其它同类项目上直接使用! 尝试1 go的镜像有,code server的镜像也好办,但搞两个镜像……算了,太麻烦! 尝试2 把code server集成到go的镜像里,由于一直追求小体积镜像,使用的是alpine版,但code server使用到了glibc,应该能改,但还是太麻烦了。 尝试3 既然把code server集成到go的镜像里太麻烦,那就把go集成到code server的镜像里。思路也很简单:集成go->导入插件->导入依赖->配置环境。 由于本机的环境就是linux+go,也有docker,而且尝试时也已安装过code server并且安装了插件,所以很多东西可以直接打包进镜像里。 几番尝试后,最终打了个开箱即用的镜像,自我感觉良好,但也有几个地方值得拿出来说一下: 由于code server的镜像里没有中文环境,所以中文语言包插件就不必折腾了,只要客户端支持中文,完全不影响使用 由于本机就是linux+vscode+go的开发环境,所以直接打包了,Dockerfile并不通用 由于追求开箱即用,是把所有所需都打进了镜像中,若需要灵活,可使用挂载卷的形式 为了降低性能损耗,同时避免调试端口的映射,容器的网络使用host模式 总结 搭建本地开发环境,安装插件,更新依赖 编写Dockerfile 1 2 3 4 5 6 7 8 9 10 FROM codercom/code-server:4.109.5 # go 插件 COPY --chown=coder:coder ./.local/share/code-server/extensions /home/coder/.local/share/code-server/extensions # go sdk COPY --chown=coder:coder ./go1.2x.x /home/coder/.local/go # go 依赖 COPY --chown=coder:coder ./go /home/coder/go # 必要的配置 RUN echo 'export PATH="$PATH:$HOME/....

2026-03-04 21:53:18 · 1 分钟 · 慢步道人

Go踩坑小结

背景 近两三个月,在项目(go)中踩了很多坑,在此汇总一下以备忘。 大坑小坑都是坑 指针 指针是个很强大的工具,日常开发中也有很多妙用,自认在delphi/lazarus中已经是得心应手了,但在go中却频频踩坑。 空指针 空指针是最近踩坑最多的,而且一不小心就会panic,动不动就被摁在地上摩擦,踩坑后也有了些心得: 能使用值变量的情况下就不要使用指针变量,除非必须使用指针变量的场景 避免var p *T这种单纯的声明指针变量,一定要在声明指针变量时立即初始化,除非期望的初始值是nil 使用指针变量前先判空 赋值 目前在go(go1.25.x)中是不能直接给指针变量赋字面量值的,必须先把字面量赋给变量,再把变量的地址赋给指针变量,如: 1 2 n := 0 p := &n 时间转换 字符串时间解析 字符串时间解析为time.Time是经常会用到的,通常使用time.Parse即可,但必须要确保时间格式与字符串时间的格式一致。可以自定义函数实现兼容多种格式: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func parseTime(timeStr string) (time.Time, error) { // 尝试多种格式 layouts := []string{ "2006-01-02 15:04:05", "2006/01/02 15:04:05", "2006-01-02", time.RFC3339, "02/01/2006 15:04:05", } for _, layout := range layouts { if t, err := time....

2026-02-02 17:05:12 · 1 分钟 · 慢步道人

Go多版本共存

场景 工作需要,一般使用的是相对较低的go版本,且较长时间内不会轻易变更;自己尝鲜或参与某个开源项目,又会使用另外的go版本。在不同项目间切换工作,通常需要切换到对应的go版本(虽然go目前是向下兼容的,高版本可以正确编译低版本,但开发人员即便熟知不同版本间的差异,也不能百分百保证不使用到高版本的特性)。 解决方案 网上也有很多方案,尝试后摸索出了比较符合自己风格和习惯的方案。 创建一个存放go不同版本的目录,用于将不同版本下载到该目录统一管理,并以版本号命名 1 mkdir $HOME/gosdk 编写下载脚本download_go.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #!/bin/bash if test "${1}" == ""; then echo '请传入正确的版本号,如:'${0}' 1.21.1' else # 统一管理目录 gosdk=$HOME/gosdk # 下载 gopkg=go${1}.linux-amd64.tar.gz if ! test -e ${gopkg}; then wget -c https://dl.google.com/go/${gopkg} fi # 删除可能冲突的文件 rm -rf ${gosdk}/go rm -rf ${gosdk}/go${1} # 解压 tar -C ${gosdk}/ -xzf ${gopkg} # 重命名 mv ${gosdk}/go ${gosdk}/go${1} # 删除安装包 rm ${gopkg} # 配置环境变量 go env -w GOPROXY=https://goproxy....

2025-05-26 10:27:45 · 1 分钟 · 慢步道人

ANSI转义序列

起因 近来,翻看gin源码时,无意间看到了green = "\033[97;42m"这种不明所以的代码,遂充满疑惑和好奇,于是就搜索探究了一番,这才知道这叫ANSI转义序列。 ANSI转义序列 简单说,就是一种标准化的终端控制序列,用于设置文本样式、颜色和背景等。先放个代码吧,以免不知所云。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package main import "fmt" func main() { for i := 0; i <= 10; i++ { fmt.Printf("这是\033[%[1]dm<文本属性>%2[1]d\033[0m\n", i) } s := []int{30, 31, 32, 33, 34, 35, 36, 37} for _, v := range s { fmt.Printf("这是\033[%[1]dm<16色标准前景色>%2[1]d\033[0m\t\033[%[2]dm<16色亮前景色>%2[2]d\033[0m\n", v, v+60) fmt.Printf("这是\033[%[1]dm<16色标准背景色>%2[1]d\033[0m\t\033[%[2]dm<16色亮背景色>%2[2]d\033[0m\n", v+10, v+70) } for i := 0; i < 256; i++ { fmt....

2025-04-17 20:28:24 · 2 分钟 · 慢步道人

Go命令行加进度条

背景 文件去重功能做好了,但当文件比较多或文件比较大的时候,耗时也会比较久,想加个进度条来直观显示处理进度。 简单尝试了下github.com/schollz/progressbar这个库,完全能满足目前的需求。 使用 安装 1 go get -u github.com/schollz/progressbar/v3 # 注意带版本v3 一般使用 1 2 3 4 5 6 bar := progressbar.Default(n, "描述") defer bar.Close() i := 0; i < n; i++ { bar.Add(1) // 工作代码 } n为总数,当n>0时,显示的是常规的进度条;当n=-1时,显示一个计数的进度。 还有其它比较细的控制及其它场景的使用,等有空了再细细研究下。

2025-03-21 21:38:23 · 1 分钟 · 慢步道人

用Go写一个文件去重工具

背景 想自己做这个功能,主要是因为Duplicate Cleaner这个商业软件只有几天的试用时间,而且文件去重这个逻辑也非常简单。 graph TD a[获取文件清单及大小] --> b[按大小分组] --> c[排除只有一个文件的组] --> d[计算文件Hash值] --> e[按Hash值分组] --> f[排除只有一个文件的组] --> g[选择需要删除的文件] --> h[删除] 问题 计算文件Hash值,使用了hash.Hash接口,自然也用到了goroutine来缩短耗时,但是在测试的时候发现功能不太好用,时好时坏,准确说是有时能获取到重复列表,有时不能。 一点点排查,并且把代码段发给DeepSeek,最终确定是因为hash.Hash不是并发安全的。 修复方法很简单,只要在goroutine内实例化即可。修改之后达到了预期。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func calcHashs(files []*FileInfo, hashName string) { g := sync.WaitGroup{} for _, file := range files { g.Add(1) go func(f *FileInfo) { defer g.Done() h := newHash(hashName) hashValue, err := calcHash(f....

2025-03-20 16:19:57 · 1 分钟 · 慢步道人

Gin功能列表

gin版本v1.10.0 包函数 方法 含义 说明 BasicAuth 创建一个HTTP基本认证(Basic HTTP Authorization)的中间件 底层调用BasicAuthForRealm BasicAuthForProxy 创建一个HTTP代理基本认证(Basic HTTP Proxy - Authorization)的中间件 BasicAuthForRealm 创建一个HTTP基本认证(Basic HTTP Authorization)的中间件 Bind 创建一个用于将请求中的数据绑定到指定的接口对象上的中间件 CreateTestContext 创建一个干净的Engine实例和一个与之关联的上下文对象用于测试 CreateTestContextOnly 在已有的Engine实例基础上创建一个独立的上下文用于测试 CustomRecovery 根据自定义的处理函数创建一个恢复中间件 底层调用CustomRecoveryWithWriter CustomRecoveryWithWriter 创建一个自定义的恢复中间件 Default 返回一个默认的Engine实例 默认含Logger和Recovery两个中间件 Dir 返回一个http.FileSystem接口的实现,该实现可被http.FileServer使用 DisableBindValidation 关闭默认的验证器 DisableConsoleColor 禁用控制台的颜色输出 EnableJsonDecoderDisallowUnknownFields 开启JSON解码器的DisallowUnknownFields功能 即遇到未知字段时就报错 EnableJsonDecoderUseNumber 开启JSON解码器的UseNumber功能 即将数字解码为json.Number类型,而不是float64,以在需要时精确地转换为整数或浮点数,从而避免精度丢失的问题 ErrorLogger 创建一个能够处理任意类型的错误的中间件 底层调用ErrorLoggerT ErrorLoggerT 创建一个能够处理指定类型的错误的中间件 ForceConsoleColor 强制在控制台输出带有颜色的内容 IsDebugging 判断当前框架是否处于调试模式 Logger 使用默认配置来创建一个日志中间件 底层调用LoggerWithConfig LoggerWithConfig 根据传入的配置来创建一个日志中间件 LoggerWithFormatter 根据指定的格式来创建一个日志中间件 底层调用LoggerWithConfig LoggerWithWriter 根据指定的输出目标创建一个日志中间件 底层调用LoggerWithConfig Mode 返回当前Gin框架的运行模式 有debug、release和test三种模式 New 返回一个全新的、没有任何中间件的Engine实例 Recovery 使用默认配置创建一个恢复中间件 底层调用RecoveryWithWriter RecoveryWithWriter 根据指定输出目标创建一个恢复中间件 底层调用CustomRecoveryWithWriter SetMode 设置gin框架的运行模式 WrapF 将标准的http....

2025-02-23 19:48:17 · 3 分钟 · 慢步道人

Go path包

path包 path包仅适用于处理由正斜杠/分隔的路径,例如URL。 不能处理带有盘符或反斜杠\的Windows路径。 包函数 方法 含义 说明 Base 返回路径的最后一个元素 会先移除路径末尾的斜杠,空路径返回.,纯斜杠返回/ Clean 返回与输入路径等效的最短路径名 Dir 返回路径的目录部分 Ext 返回路径中以.分隔的文件扩展名 无.则返回空字符串 IsAbs 判断路径是否为绝对路径 只有以/开头的才是绝对路径 Join 将路径元素连接成路径 Match 判断路径是否匹配模式 Split 将路径拆分为目录和文件 filepath包 处理方式与目标操作系统定义的文件路径相兼容。 包函数 方法 含义 说明 Abs 返回绝对路径 Base 返回路径的最后一个元素 会先移除路径末尾的斜杠,空路径返回.,纯斜杠返回/ Clean 返回与输入路径等效的最短路径名 Dir 返回路径的目录部分 EvalSymlinks 返回路径中的符号链接所指向的真实路径 Ext 返回路径中以.分隔的文件扩展名 无.则返回空字符串 FromSlash 将路径中/的斜杠替换为特定操作系统的分隔符字符 linux中的\不会被替换 ToSlash 将路径中特定操作系统的分隔符字符替换为/ linux中的\不会被替换 Glob 返回与模式匹配的所有路径 IsAbs 判断路径是否为绝对路径 只有以/开头的才是绝对路径 IsLocal 判断路径是否为本地路径,即是否在当前路径下 仅词法分析,不考虑文件系统 Join 将路径元素连接成路径 Localize 将一个以/分隔的路径转换为一个操作系统路径 Match 判断路径是否匹配模式 Rel 返回从basepath到targpath的相对路径 Split 将路径拆分为目录和文件 SplitList 将一个包含多个以特定操作系统路径分隔符(如:或;)分隔的路径字符串拆分成单个的路径元素 分隔符取决于当前操作系统 VolumeName 返回路径的卷名 适用于Windows平台 Walk 遍历路径下的所有目录和文件并对其调用fn WalkDir 遍历路径下的所有目录和文件并对其调用fn 比Walk更优更高效

2024-11-03 22:12:48 · 1 分钟 · 慢步道人

Go sync包

Mutex 无需显式初始化,直接声明变量即可使用。 互斥锁,同一时刻只能有一个协程持有锁,不分读写。 方法 含义 说明 Lock 加锁 Unlock 解锁 未加锁时调用会导致panic TryLock 尝试加锁 立即返回,成功返回true,失败返回false RWMutex 无需显式初始化,直接声明变量即可使用。 读写锁,同一时刻可以有多个协程持有读锁,但是只能有一个协程持有写锁。 有写锁时,其他协程无法获取读锁和写锁。 有读锁时,其他协程可以获取读锁,但是无法获取写锁。 方法 含义 说明 RLock 加读锁 RUnlock 解读锁 未加读锁时调用会导致panic TryRLock 尝试加读锁 立即返回,成功返回true,失败返回false Lock 加写锁 Unlock 解写锁 未加写锁时调用会导致panic TryLock 尝试加写锁 立即返回,成功返回true,失败返回false Map 无需显式初始化,直接声明变量即可使用。 方法 含义 说明 Store 存储键值对 Load 加载键对应的值 Delete 删除键对应的值 Clear 清除所有键值对 Swap 交换指定键对应的旧值和新值,并返回旧值和操作之前键是否存在 LoadAndDelete 加载键对应的值并删除键值对 LoadOrStore 加载键对应的值,如果键不存在则存储键值对 CompareAndDelete 比较键对应的值是否等于指定值,如果相等则删除键值对 CompareAndSwap 比较键对应的值是否等于指定值,如果相等则替换键对应的值 Range 遍历键值对 传入函数返回false时停止遍历 WaitGroup 无需显式初始化,直接声明变量即可使用。 方法 含义 说明 Add 添加计数器 启动协程前调用,传入协程数量 Done 减少计数器 协程执行完毕后调用 Wait 等待计数器变为0 主协程调用,等待所有协程执行完毕 Once 无需显式初始化,直接声明变量即可使用。 方法 含义 说明 Do 执行函数 传入函数,只会执行一次 Pool 需显式初始化,为New指定创建对象的函数。...

2024-10-30 22:45:48 · 1 分钟 · 慢步道人