惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园 - Franky
月光博客
月光博客
T
Tenable Blog
博客园 - 聂微东
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
IT之家
IT之家
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CXSECURITY Database RSS Feed - CXSecurity.com
F
Full Disclosure
博客园 - 司徒正美
Project Zero
Project Zero
Y
Y Combinator Blog
A
Arctic Wolf
美团技术团队
博客园 - 叶小钗
S
Securelist
F
Fortinet All Blogs
T
Threatpost
T
Threat Research - Cisco Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
酷 壳 – CoolShell
酷 壳 – CoolShell
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
Recorded Future
Recorded Future
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Schneier on Security
Apple Machine Learning Research
Apple Machine Learning Research
L
LangChain Blog
P
Privacy International News Feed
博客园_首页
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
P
Proofpoint News Feed
Cisco Talos Blog
Cisco Talos Blog
AWS News Blog
AWS News Blog
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
雷峰网
雷峰网
P
Proofpoint News Feed
Latest news
Latest news
G
GRAHAM CLULEY

文章列表 on polarisxu

通过一个例子让你彻底掌握 Go 工作区模式 这道题正确率竟然只有 22% Go 开源项目推荐:一个简单的 Go 练手项目 go test 如何禁用缓存? Go1.18 快讯:constraints 包被移除标准库 跟着 Go 作者学泛型 「2022 版」轻松搞定 Go 开发环境 鹅厂小哥用行动反对加班:程序员加班能被终结吗? Go 为什么选择 Gopher 作为吉祥物? 本地如何配置多个 GitHub/Gitee 账号? 为 Java 开发者准备的 Go 教程 02:Java 有而 Go 无 GitHub 发现了 studygolang 项目依赖的漏洞 Typora 宣布收费后,这款开源 Markdown 编辑器火了 为 Java 开发者准备的 Go 教程 01:漫游了 我的 2021 年终总结 泛型版 singleflight:Go 中如何防止缓存击穿? 2021 年你写了多少代码?这个 Go 工具帮你统计 Go1.18 新特性:TryLock Go 泛型入门教程 程序员瑞士军刀:各种结构的转换工具 终于有 Go 版的 Elasticsearch 了 Go泛型系列:maps 包讲解 Go 1.18 中的 any 是什么? Go1.18 快讯:新的 IP 包 Go编程模式:详解函数式选项模式 Go泛型系列:slices 包讲解 Nginx 竟然也有 playground — Go 语言构建的 Go泛型系列:Go1.18 类型约束那些事 Go1.18 快讯:Module 工作区模式 Go1.18 快讯:新增的 Cut 函数太方便了 Go1.18 快讯:废弃了这个 API Go1.18 快讯:新增字符串 Clone API Go 如何获取和设置环境变量 重磅变动:更快找到微信公众号里的Go文章 Go 这样设置版本号:我们的项目也可以 假期结束,推荐 2 本 manning 出的 Go 图书 Go Fiber 框架系列教程 04:测试应用 Go Fiber 框架系列教程 03:中间件 Go泛型系列:提前掌握Go泛型的基本使用 推荐一个 Go GUI 实战项目 Go Fiber 框架系列教程 02:详解相关 API 的使用 Go Fiber 框架系列教程 01: 和 Express 对比学习 Go Module:私有不合规库怎么解决引用问题 Go1.17 新特性:go get 变了 Go1.17 新特性:testing 包的相关变化 StackOverflow 上关于 Go select 死锁的问题 新书推荐:用 Gin 框架构建分布式应用 Go 官网要变天。。。 Gin 这是要成为 Go 官方框架? 扬眉吐气:刚刚,Go 已经默认支持泛型了 Go 1.17 新特性:Module 有哪些变化? Go 第三方库推荐:类型转换如此简单 担心密码提交到 GitHub?建议使用这个 Go 开源工具 Go 的时间格式化为什么是 2006-01-02 15:04:05? GitHub 为 Go 社区带来安全支持 Go开源项目推荐:500行代码确认请求时间花在哪 一道关于 len 函数的诡异 Go 面试题解析 厉害了我的 Go:推荐系统都有开源实现 Go1.17 新特性:新版构建约束 这个工具真好:看看你的Go项目依赖有无漏洞 网友很强大,发现了Go并发下载的Bug Rust 劝退系列 09:函数 Go项目实战:一步步构建一个并发文件下载器 这本 Go 新书挺期待的:100 个常见错误 Go 启用新的官方问答社区 这是要干嘛?!微软招 Go 编译器全职开发人员 Go1.17 快报之标准库越来越注重易用性 Go1.17 新特性之切片变数组 回顾 Go 官网的演变史 站长8年前的Go代码竟然进入大厂的项目里了 Go图书翻译:一个好消息,一个坏消息 不怕烂尾!决定组织翻译这本 Go 图书 Rust 劝退系列 08:模式匹配 一本花了2.5年写成的Go免费在线图书 被黑惨了:一句话,说明自己会 Go,咋整? Go1.16 中的新函数 signal.NotifyContext 怎么用? Go 1.17 新特性提前学之测试执行随机化 Rust 劝退系列 07:流程控制 官方的 Go 多版本管理:使用和原理 Rust新书:给你一个劝退的理由 周刊题解:常量表达式这个规则应该了解下 盘点那些使用 Go 语言的国外公司 编写了50万行Go代码是一种什么体验 Rust 劝退系列 06:常量 Uber 使用 Go 的规模这么大?!都自己定制的 Go 编译器了 Go Team Leader — rsc 大神新开源了一个库,增强模板功能 再一次看到了 Go 的节制:int128 类型要不要支持? Rust 劝退系列 05:复合数据类型 「卷」有理论依据:海勒姆定律—Go又是怎么卷的 Go 真的也可以进行 GUI 开发:还有这样的图书呢 Rust 劝退系列 04:基本数据类型 我的 Go 语言书单 Rust 劝退系列 03:变量 我又来推荐免费 Go 新书了:一本用 Go 讲架构的书 注释竟然还有特殊用途?一文解惑 //go:linkname 指令 这个功能,公众号、微信群会不会被玩坏? 一道 Go 闭包题,面试官说原来自己答错了:面别人也涨知识 Rust 劝退系列 02:第一个 Rust 程序 Rust 劝退系列 01:打造开发环境 答应我,这次一定彻底搞懂 Go 中的类型别名
Go:如何获得项目根目录?
2021-10-31 · via 文章列表 on polarisxu

大家好,我是 polarisxu。

项目中,特别是 Web 项目,经常需要获得项目的根目录,进而可以访问到项目相关的其他资源,比如配置文件、静态资源文件、模板文件、数据文件、日志文件等(Go1.16 后,有些可以方便的通过 embed 内嵌进来)。比如下面的目录结构:(路径是 /Users/xuxinhua/stdcwd

├── bin
    ├── cwd
├── main.go
└── log
    ├── error.log

为了正确读取 error.log,我们需要获得项目根目录。学完本文知识可以解决该问题。

解决方案有多种,各有优缺点和使用注意事项,选择你喜欢的即可。

01 使用 os.Getwd

Go 语言标准库 os 中有一个函数 Getwd

func Getwd() (dir string, err error)

它返回当前工作目录。

基于此,我们可以得到项目根目录。还是上面的目录结构,切换到 /Users/xuxinhua/stdcwd,然后执行程序:

$ cd /Users/xuxinhua/stdcwd
$ bin/cwd

这时,当前目录(os.Getwd 的返回值)就是 /Users/xuxinhua/stdcwd

但是,如果我们不在这个目录执行的 bin/cwd,当前目录就变了。因此,这不是一种好的方式。

不过,我们可以要求必须在 /Users/xuxinhua/stdcwd 目录运行程序,否则报错,具体怎么做到限制,留给你思考。

02 使用 exec.LookPath

在上面的目录结构中,如果我们能够获得程序 cwd 所在目录,也就相当于获得了项目根目录。

binary, err := exec.LookPath(os.Args[0])

os.Args[0] 是当前程序名。如果我们在项目根目录执行程序 bin/cwd,以上程序返回的 binary 结果是 bin/cwd,即程序 cwd 的相对路径,可以通过 filepath.Abs() 函数得到绝对路径,最后通过调用两次 filepath.Dir 得到项目根目录。

binary, _ := exec.LookPath(os.Args[0])
root := filepath.Dir(filepath.Dir(filepath.Abs(binary)))

03 使用 os.Executable

可能是类似的需求很常见,Go 在 1.8 专门为这样的需求增加了一个函数:

// Executable returns the path name for the executable that started the current process.
// There is no guarantee that the path is still pointing to the correct executable.
// If a symlink was used to start the process, depending on the operating system, the result might be the symlink or the path it pointed to.
// If a stable result is needed, path/filepath.EvalSymlinks might help.
// Executable returns an absolute path unless an error occurred.
// The main use case is finding resources located relative to an executable.
func Executable() (string, error)

和 exec.LookPath 类似,不过该函数返回的结果是绝对路径。因此,不需要经过 filepath.Abs 处理。

binary, _ := os.Executable()
root := filepath.Dir(filepath.Dir(binary))

注意,exec.LookPath 和 os.Executable 的结果都是可执行程序的路径,包括可执行程序本身,比如 /Users/xuxinhua/stdcwd/bin/cwd

细心的读者可能会注意到该函数注释中提到符号链接问题,为了获得稳定的结果,我们应该借助 filepath.EvalSymlinks 进行处理。

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    ex, err := os.Executable()
    if err != nil {
        panic(err)
    }
    exPath := filepath.Dir(ex)
    realPath, err := filepath.EvalSymlinks(exPath)
    if err != nil {
        panic(err)
    }
    fmt.Println(filepath.Dir(realPath))
}

最后输出的就是项目根目录。(如果你的可执行文件放在根目录下,最后的 filepath.Dir 就不需要了)

注意:exec.LookPath 也有软链接的问题。

exec.LookPath 和 os.Executable 函数,再提醒一点,如果使用 go run 方式运行,结果会是临时文件。因此,记得先编译(这也是比 go run 更好的方式,go run 应该只是用来本地测试)。

04 小结

既然 Go1.8 为我们专门提供了这样一个函数,针对本文提到的场景,我们应该总是使用它。

你用的什么方式呢?欢迎留言交流。