软件下载
https://golang.google.cn/ https://dl.google.com/go/go1.20.5.windows-amd64.msi
环境变量配置
我的电脑--属性--环境变量-用户环境变量 添加一下 GOWKS=G:\tpf\gowks GOROOT=%GOWKS%\app\gowin GOBIN=%GOWKS%\bin GOPATH=%GOWKS% 将下面的路径添加到path中 %GOROOT%\bin %GOBIN%
软件下载
https://dl.google.com/go/go1.19.3.linux-amd64.tar.gz https://studygolang.com/dl/golang/go1.18.10.linux-amd64.tar.gz https://golang.google.cn/
Go环境变量设置
export GOWKS=/opt/wks/gowks export GOROOT=$GOWKS/app/go export GOBIN=$GOWKS/bin export GOPATH=$GOWKS export PATH=$GOROOT/bin:$GOBIN:$PATH GOWKS是自定义的GO工作空间, GO软件安装在其app/go目录下, GOWKS同时也是GOPATH, 这里面包含项目运行的GO软件,下载的依赖包,编辑后的命令,说明文档等一切项目相关的文件。 后续若进行项目迁移,不管是GO本身版本变化,还是它的依赖包版本变化, 直接将这个目录COPY走就解决了所有问题。 GO ENV go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct # 国内七牛云提供
|
go mod mkdir esql cd esql go mod init 就完成go mod的初始化化了 go mod tidy 这一步自动将go文件引入的外部包写入mod文件,并修改sum文件 如果要使用或找不到本地包,则修改mod相应的包指向本地目录位置 require gitee.com/ol v0.0.0-00010101000000-000000000000 // indirect replace gitee.com/ol => ../../ol |
|
go vendor 进入包目录,执行 go mod vendor 就可以将依赖的外部包打包一份到vendor目录下,repalce指向本地的除外 这里要求你的项目放在GOPATH/src目录下 依赖于GOPATH,可放于GOPATH/src目录下,或者GOPATH/src的项目目录下 查找顺序:从当前模块目录找vendor目录,一直找到GOPATH/src 使用:手动依赖包或自己写的模块放到vendor目录,或者mod模式下进入目录执行go mod vendor 好处:下载的依赖包随项目走,不用关心这些依赖包的版本变化导致项目不可用了 注意事项 go vendor是要放在gopath/src目录下的 如果gopath/src/vendor与gopath/src下存在相同的模块,则vendor目录的优先级高, 即会调用vendor下的模块 而go mod的模块目录如果使用replace指定特定目录后,就不会走vendor了 如果使用go mod编辑报错同时又是个人项目,可以关闭go mod:go env -w GO111MODULE=off 没了go mod的自动下载,就将手工将下载的依赖包放到vendor下, 项目迁移时,vendor随之迁移,这样的项目也是极其稳定的, 或者像文章开始那样搭建项目,项目相关所有文件都放在$GOWKS目录下, 迁移时,GO版本与其依赖包都不变化,这样就万无一失了。 如果没有使用go mod,那么下载并编辑项目github.com/kardianos/govendor,生成一个govendor go get -u -v github.com/kardianos/govendor go mod init go mod tidy go build govendor用法 govendor init 初始化vendor目录 govendor add +external 添加外部依赖 govendor update +external 更新外部依赖 govendor update $PACKAGE_NAME 更新指定包的依赖 |
|
|
|
|
|
|
|
|
|
|
|
|
export GOWKS=/opt/tpf/liu export GOROOT=$GOWKS/app/go export GOBIN=$GOWKS/bin export GOPATH=$GOWKS export PATH=$GOROOT/bin:$GOBIN:$PATH
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct # 国内七牛云提供
GO有四种数据类型,数字、布尔、字符、派生类型;这里使用 前三种 简单类型 来说明变量的定义与使用
int8/uint8
int16/uint16
int32/uint32
int64/uint64
float32
float64
complex64
bool
byte
rune :32位有符号整型,存储Unicode字符
int/uint :是32位还是64位看平台,其他的通过名字就能看到多少位
uint8 0-255,int8则是-128-127,这两个是Go的最小长度类型,其他类型在内存中分配的长度都要比这两个大;
--------------------------------------------
package main
import "fmt"
var (
aa = 1
bb = true
ss = "作用域为包的变量"
s2,s3 = "包变量的位置可能写在包内的任何地方","即可以是引用的前面也可以在引用的后面"
)
//GO有四种类型,数字、布尔、字符、派生类型
//变量的默认值
func var1(){
var a int
var b bool
var f float64
var s string
// %d 表示数字,%s表示字符串,%q可以为字符串带上双引号
fmt.Printf("%d-%s-%q\n",a,s,s) // 0--""
fmt.Println(b) //false
fmt.Println(f) //0
}
//变量初始化
func var2(){
var a int = 100 //明确地写出了变量的类型
var b,f,s = true,3.14,"批评别人时,一定要顾及听者的面子" //不定义类型,同时多变量写于一行
fmt.Println(a,b,f,s)
var aa = 300
var ss = "在回答别人或对别人发表观点时,一定要先准确理解别人的意思"
fmt.Println(aa,ss)
}
//较常用的变量声明赋值方式
func var3(){
//多行合并为一行,省去了类型,连var关键字也省去了,编辑器自动判断变量类型;只能用于一次声明变量时使用
s1,s2 := "话越多","越没有份量"
fmt.Println(s1,s2)
//第二次使用时,不可再加冒号
s1,s2 = "讲话时不能只顾着自己说","更要注意对方的感受和情绪变化"
fmt.Printf("%s,%q",s1,s2) //讲话时不能只顾着自己说,"更要注意对方的感受和情绪变化"
}
func main() {
var1()
var2()
var3()
}
变量的命名: 推荐使用studentName,而不是student_name 首字母大写有特殊的含义,比如一个包的方法要想在其他包中使用,那么该方法名称必须首字母大写,又比如结构体中的属性等 变量的作用域: 比如包a下有b.go与c.go,b.go中有方法C,则其他包引用的方式为a.C() 包,即文件所在目录的名称,包名文件夹下可以有多个文件,包内变量的作用域是可以跨越这些文件的; 比如,一个包中不允许存在两个相同名称的方法,会提示重复; 函数内的变量的作用域就只是在函数内了 字符串 字符串分为原生字符串、解释型字符串两种; 原生字符串使用``(反引号),解释型字符串使用双引号"",解释型字符串中可以解析转义字符;
package main
import (
"fmt"
"math"
)
//常量
func cons(){
const a,b= 3,4 //常量的数字在不明确指定类型的情况下,即可以作为int使用,也可以作为float32/float64使用
var c int
c = int(math.Sqrt(a*a+b*b)) //不需要转float64
fmt.Println(c) //5
const aa,bb int = 3,4
cc := math.Sqrt(float64(aa*aa + bb*bb))
fmt.Println(cc) //5
fmt.Println(s1,s2) //你多少次在讲话前 没有去在意对方的心思
}
//包内常量,go的常量通常不全部大写表示,这是因为go语言定义的变量或常量,首字母大写是有特定含义的
const (
s1,s2 ="你多少次在讲话前","没有去在意对方的心思"
)
//枚举
func enum(){
//在go中可以简单地使用const常量来表示枚举
const(
java = 0
net = 1
c =2
rust = 3
)
const(
aa = iota //一个行计数器,从0开始,刚好与枚举从零开始相符,故而使用iota简化枚举的写法
bb
_ //_一个占位符,这样下面的dd才能被赋值3
dd
)
const(
byte = 1 <<(10 * iota)
kb
mb
gb
tb
pb
)
fmt.Println(byte,kb,mb,gb,tb,pb) //1 1024 1048576 1073741824 1099511627776 1125899906842624
}
func main() {
cons()
enum()
}
数组
var arr1 = [4]uint8{192,168,0,1}
var arr2 = [...]uint8{192,168,0,2}
...,三个小黑点,由Go编译器计算该值的元素数量并以此获得其长度,也适用于函数参数
切片
var slic1= []string{"192.168.0.1","192.168.0.2"}
var slic2=append(slic1,"192.168.0.3")
var slic3=make([]string,50)
append可以为切片类型[]string追加元素
make可以高效地为一个切片初始化数据,make([]string,50)则是一次性初始化50个默认值为""的元素
len与cap
func main(){
println(len(arr1),cap(arr2),len(slic2),cap(slic3))
}
输出
4 4 3 50
package main
var m1 = map[string]bool{}
func main(){
m1["魔镜,我是世界上最美的女人吗"]= false
println(m1["魔镜,我是世界上最美的女人吗"])
println(len(m1))
delete(m1,"魔镜,我是世界上最美的女人吗")
println(len(m1))
delete(m1,"魔镜,我是世界上最美的女人吗")
println(len(m1))
}
delete可以删除本身就不存在的元素,输出
false
1
0
0
相比数组的定义,
map多了关键字map,
以及key的类型,放在了中括号里
map[key type]value type{}
for循环
for i:= 0;i<9;i++ {
fmt.Println(i)
}
for循环数组
for index, val := range arr{
fmt.Printf("第 %d 位 x 的值 = %d\n", index, val)
}
for循环map
for key, value := range oldMap {
newMap[key] = value
}
for 永远循环,死循环中通常会有跳出循环的条件
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
for i:= 0;i<r.FieldsPerRecord;i++ {
fmt.Println(i,record[i])
}
}
if 条件语句
package main
import (
"fmt"
"io/ioutil"
)
func if1(){
const filename = "/tmp/a.log"
if txt, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", txt)
}
}
go switch: 找到第1个条件成立的分支就返回
package main
import (
"fmt"
)
func switch1(level int) string{
res := ""
switch level{
case 1:
res = "100000"
case 2:
res = "30000"
case 3:
res = "10000"
default:
res = "1000"
}
return res
}
func switch2(level int) string{
res := ""
switch {
case level == 100:
res = "100000"
case level > 90:
res = "30000"
case level > 80:
res = "10000"
case level > 60:
res = "1000"
case level <= 60:
panic("You ......")
}
return res
}
func main() {
res := switch1(1)
println(res) //100000
fmt.Println(switch1(1),switch1(2)) // 100000 30000
fmt.Println(switch2(100),switch2(88)) // 100000 10000
//下面这个是直接报错,并不会输出前两个到第三个时再报错
fmt.Println(switch2(100),switch2(88),switch2(38))
}
结构体定义示例
变量 : var name 数据类型
常量 : const name 数据类型
结构体: type name 数据类型 --> type Student struct
从上面三行感受一下GO语言变量设计的风格:
第一个关键词 表示要定义的事物属于哪个大类,是变量,还是常量,还是本身就是一个类型
第二个关键词 表示要定义的事物的名称
第三个关键词 表示细分的数据类型,是简单类型数字,还是复合类型 字符串,结构体等
package main
import (
"fmt"
)
type Student struct {
id int
name string
}
func (st *Student) ShowName(){
st.name = "花开12瓣,瓣瓣为一生"
fmt.Println(st.name)
}
func main(){
s1 := Student{}
s1.ShowName()
}
常用格式化输出
package main
import (
"fmt"
)
func main(){
fmt.Println("我来人间一趟")
fmt.Printf("%s\n","本来无念无想")
ss := fmt.Sprintf("%v","一入红尘就变了模样")
println(ss)
}
%d 表示十进制
%e 科学计数法,例如 -1.4e+11
%f 小数点
%o 表示八进制
%s 字符串表示(string类型或[]byte)
%p 十六进制内存地址,前缀ox
%v 通用格式,按数据本身的类型输出,比如是字符串,此时%v自动转为%s的效果
package main
import (
"fmt"
)
type Student struct {
id int
name string
}
func main(){
s1 := Student{id:1,name:"韩立"}
fmt.Printf("%v\n",s1) // {1 韩立}
fmt.Printf("%+v\n",s1) // {id:1 name:韩立}
fmt.Printf("%#v\n",s1) // main.Student{id:1, name:"韩立"}
fmt.Printf("%T\n",s1) // main.Student
}
数字之间及字符串的转化
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
//查看一个数据的类型
fmt.Println(reflect.TypeOf("123")) // string
fmt.Println(reflect.TypeOf(1.23)) // float64
strconv.Atoi("123") // 字符串转数字
strconv.Itoa(123) // 数字转字符串
strconv.FormatUint(uint64(123), 8) // 10进制转8进制
strconv.FormatUint(uint64(123), 16)// 10进制转16进制
strconv.FormatFloat(float64(1.23), 'e', 4, 32) //10进制转float32,保留4位有效数字,e表示科学计算法
strconv.FormatFloat(1.23, 'f', 4, 64) //10进制转float64,保留4位有效数字,f表示浮点数
}
依赖库
github.com/godror/godror database/sql
客户端安装
oracle客户端,instantclient-basic-linux.x64-11.2.0.4.0.zip rpm -qa | grep libaio yum install libaio 动态库设置: sudo sh -c "echo /opt/app/instantclient_11_2 > /etc/ld.so.conf.d/oracle-instantclient.conf" sudo ldconfig $ cat /etc/ld.so.conf.d/oracle-instantclient.conf /opt/app/instantclient_11_2
root@kl:/opt/wks/gowks/src/cmd# dcService -c /etc/dc.cnf dcService: /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by dcService) dcService: /usr/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by dcService) # ldd --version ldd (Ubuntu GLIBC 2.31-0ubuntu9.7) 2.31 # gcc -dumpversion 9 当初build时的glibc版本高于现在运行的 所以,build的glibc版本不要太高,不要高于使用者的glibc版本
|
|
简记
$ go tool dist list | grep windows
windows/386
windows/amd64
windows/arm
windows/arm64
# 基础静态编译命令
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o app.exe main.go
CGO_ENABLED=0 禁用 CGO,确保静态链接(不依赖 mingw-w64)
GOOS=windows 目标操作系统:Windows
GOARCH=amd64 目标架构:64位(常用)可选:386 (32位), arm64 (ARM64)
-ldflags="-s -w" -s:去除符号表 -w:去除调试信息,可减小体积约 30%
-o app.exe 输出文件名(必须带 .exe 后缀)
示例
cd /ai/wks/gowks/src/cmd/fibu2
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o fibu.exe fibu2.go
参考 我来详细介绍如何在 Ubuntu 上将 Go 程序交叉编译为 Windows 平台的静态可执行文件(包含所有依赖)。 ## 核心命令 ```bash # 基础静态编译命令 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o app.exe main.go ``` --- ## 参数详解 | 参数 | 说明 | |------|------| | `CGO_ENABLED=0` | **禁用 CGO**,确保静态链接(不依赖 mingw-w64) | | `GOOS=windows` | 目标操作系统:Windows | | `GOARCH=amd64` | 目标架构:64位(常用) |
gitee.com/tanpf/gin/dc/client
→ gitee.com/tanpf/tools
→ github.com/ThomasRooney/gexpect (依赖 CGO 的 expect 库)
gexpect 是一个用于自动化交互式命令行的库,底层依赖系统的 C 库,无法静态编译。
详细
这个错误是因为 `github.com/ThomasRooney/gexpect` 库使用了 CGO(调用了 C 语言的 expect 库),而你设置了 `CGO_ENABLED=0` 禁用了 CGO,导致编译失败。
## 问题根源
```
gitee.com/tanpf/gin/dc/client
→ gitee.com/tanpf/tools
→ github.com/ThomasRooney/gexpect (依赖 CGO 的 expect 库)
```
`gexpect` 是一个用于自动化交互式命令行的库,底层依赖系统的 C 库,无法静态编译。
---
## 解决方案
### 方案 1:替换为纯 Go 实现的交互式命令行库(推荐)
找到 `gitee.com/tanpf/tools` 中使用 `gexpect` 的地方,替换为纯 Go 库:
| 原库 | 替换为 | 说明 |
|------|--------|------|
| `github.com/ThomasRooney/gexpect` | `github.com/creack/pty` + `os/exec` | 纯 Go 实现 PTY |
| | `github.com/google/goexpect` | Google 的纯 Go expect 实现 |
| | `github.com/alessio/shellescape` | 简单的命令转义 |
**示例替换**:
```go
// 原代码(使用 gexpect)
import "github.com/ThomasRooney/gexpect"
child, _ := gexpect.Spawn("ssh user@host")
child.Expect("password:")
child.SendLine("mypass")
// 新代码(使用 creack/pty)
import (
"github.com/creack/pty"
"os/exec"
)
cmd := exec.Command("ssh", "user@host")
ptmx, _ := pty.Start(cmd)
// 读写 ptmx 实现交互
```
### 方案 2:条件编译(隔离 CGO 依赖)
修改 `gitee.com/tanpf/tools` 的代码,使用**构建标签**隔离 CGO 依赖:
```go
// +build cgo
// tools_cgo.go
package tools
import "github.com/ThomasRooney/gexpect"
func UseExpect() {
// 使用 gexpect 的代码
}
```
```go
// +build !cgo
// tools_nocgo.go
package tools
import "errors"
func UseExpect() error {
return errors.New("expect 功能需要启用 CGO 编译")
}
```
这样 `CGO_ENABLED=0` 时会自动选择无 CGO 的实现。
### 方案 3:使用 mingw-w64 交叉编译(保留 CGO)
如果你必须使用 `gexpect`,可以安装 Windows 编译器:
```bash
# 安装 mingw-w64
sudo apt-get update
sudo apt-get install mingw-w64
# 使用 mingw 编译(注意:生成的 exe 会依赖 libgcc.dll)
CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows GOARCH=amd64 \
go build -o fibu.exe fibu2.go
```
**缺点**:生成的 `fibu.exe` 需要携带 `libgcc_s_seh-1.dll` 和 `libwinpthread-1.dll`,不是纯静态。
**完全静态化(需额外参数)**:
```bash
CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows GOARCH=amd64 \
go build -ldflags="-s -w -linkmode external -extldflags '-static'" \
-o fibu.exe fibu2.go
```
### 方案 4:移除或替换依赖库
检查你的代码是否真的需要 `gitee.com/tanpf/gin/dc/client` 中的 expect 功能:
```bash
# 查看依赖关系
go mod why github.com/ThomasRooney/gexpect
# 查看哪些文件导入了它
grep -r "gexpect" /ai/wks/gowks/pkg/mod/gitee.com/tanpf/tools*
```
如果不需要该功能,可以:
1. Fork `gitee.com/tanpf/tools` 移除 expect 相关代码
2. 使用 `replace` 指令替换为修改后的版本
```go
// go.mod
replace gitee.com/tanpf/tools => github.com/yourname/tools v0.0.1
```
---
## 快速诊断命令
```bash
# 1. 确认哪些包需要 CGO
go list -f ': ' -deps . | grep -v "[]$"
# 2. 查看 gexpect 的依赖
go mod graph | grep gexpect
# 3. 尝试找出替代路径
go mod why -m github.com/ThomasRooney/gexpect
```
---
## 推荐解决路径
1. **首选**:联系 `gitee.com/tanpf/tools` 维护者,建议替换为 `github.com/google/goexpect`(纯 Go)
2. **次选**:Fork 后自行修改,移除 expect 功能或使用条件编译
3. **备选**:如果 expect 是核心功能且必须保留,使用 mingw 编译并接受 DLL 依赖,或在 Linux 服务器端运行该功能
需要我帮你查看具体的替换代码实现吗?
|
|
|
|
|
oracle客户端下载-夸克网盘