0%

写 Go 代码的时候,我期望将 cmd 的错误信息打印出来,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
var cmdOut, cmdErr bytes.Buffer
cmd.Stdout = &cmdOut
cmd.Stderr = &cmdErr
err := cmd.Run()
if err != nil {
log.Fatal(err)
// log.Println(err)
}
cmdStdout := ConvertByte2String([]byte(cmdOut.String()), "GB18030")
cmdStderr := ConvertByte2String([]byte(cmdErr.String()), "GB18030")
fmt.Println("cmdStdout:", cmdStdout)
fmt.Println("cmdStderr:", cmdStderr)

ping 一个不通的域名产生了报错,什么都没有打印出来,而是给了个返回码2。

当时我没有意识到这个问题,把代码发到群里,一个大佬 Cavan.xu 跑了一下代码,指出了问题出现在 log.Fatal 上。

改了一下,程序如期运行。

1
2
// log.Fatal(err)
log.Println(err)

看一下这个 log.Fatal 源码。Fatal 等价于 Print() 然后调用 os.Exit(1)。

1
2
3
4
5
// Fatal is equivalent to Print() followed by a call to os.Exit(1).
func Fatal(v ...interface{}) {
std.Output(2, fmt.Sprint(v...))
os.Exit(1)
}

go 的并发运行比想象的要简单很多,在要运行的语句前面加上 go就可以了

1
2
3
4
5
6
go func() {
// 在这里运行
}()
go func() {
// 在这里运行
}()

网上找的方法还未实验

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"fmt"
)

func main() {
ConcurrentWork(5, func() {
// ...业务逻辑
fmt.Println("https://blog.csdn.net/LitongZero")
})
}

/**
* @Description: 开启多线程执行
* @param total 启动线程数
* @param work 需要执行的方法
*/
func ConcurrentWork(total int, work func()) {

c := make(chan int) //设置多线程通道

// 循环创建线程
for i := 0; i <= total; i++ {
go doMyWork(i, c, work)
}

// 等待所有线程结束
for i := 0; i <= total; i++ {
fmt.Printf("第 %d 项执行完毕\n", <-c)
}
}

func doMyWork(i int, page chan int,work func()) {
fmt.Printf("第 %d 项正在执行\n", i)
work()
// 返回执行完毕
page <- i
}

在 go 中使用 cron

官方文档 - https://pkg.go.dev/github.com/robfig/cron#section-readme

这篇文章写的也不错 - https://segmentfault.com/a/1190000023029219

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"time"

"github.com/robfig/cron/v3"
)

func main() {
c := cron.New()

c.AddFunc("@every 1s", func() {
fmt.Println("tick every 1 second")
})

c.Start()
time.Sleep(time.Second * 5)
}

更新到V3,要注意文档中说的

The v1 branch accepted an optional seconds field at the beginning of the cron spec. This is non-standard and has led to a lot of confusion. The new default parser conforms to the standard as described by the Cron wikipedia page.

UPDATING: To retain the old behavior, construct your Cron with a custom parser:

1
2
3
4
5
6
7
// Seconds field, required
cron.New(cron.WithSeconds())

// Seconds field, optional
cron.New(cron.WithParser(cron.NewParser(
cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor,
)))

期望go的程序一直运行不中断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"os"
"os/signal"
"time"

"github.com/robfig/cron"
)

func main() {
c := cron.New()
c.AddFunc("* * * * * *", RunEverySecond)
go c.Start()
sig := make(chan os.Signal)
signal.Notify(sig, os.Interrupt, os.Kill)
<-sig

}

func RunEverySecond() {
fmt.Printf("%v\n", time.Now())
}

关键部分在这一块代码,还没吃透。

1
2
3
sig := make(chan os.Signal)
signal.Notify(sig, os.Interrupt, os.Kill)
<-sig

另外一个方法 select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"time"
)

func main() {
go forever()
select {} // block forever
}

func forever() {
for {
fmt.Printf("%v+\n", time.Now())
time.Sleep(time.Second)
}
}

更多方法见 how-best-do-i-keep-a-long-running-go-program-running

终端里面跑ping命令的时候,终端输出的字符通过go程序再打印出来就乱码了,要做转换处理

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import (
"bytes"
"fmt"
"github.com/commander-cli/cmd"
"golang.org/x/text/encoding/simplifiedchinese"
"log"
"os/exec"
)

type Charset string

const (
UTF8 = Charset("UTF-8")
GB18030 = Charset("GB18030")
)

func ConvertByte2String(byte []byte, charset Charset) string {
var str string
switch charset {
case GB18030:
var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
str = string(decodeBytes)
case UTF8:
fallthrough
default:
str = string(byte)
}
return str
}

func main() {
cmd := exec.Command("ping", "www.baidu.com")
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
newerr := cmd.Run()
if newerr != nil {
log.Fatal(newerr)
}
cmdStdout := ConvertByte2String([]byte(outb.String()), "GB18030")
cmdStderr := ConvertByte2String([]byte(errb.String()), "GB18030")
fmt.Println("out:", cmdStdout, "err:", cmdStderr)
}

想看一下环境变量是什么,

Windows 系统

Powershell 查看或者设置环境变量

查看所有环境变量

1
2
3
ls env:
Get-Item Env:
Get-ChildItem env:

搜索环境变量

1
ls env:NODE\*

查看单个环境变量

1
$env:NODE_ENV

添加/更新环境变量

1
$env:NODE_ENV=development

删除环境变量

1
del evn:NODE_ENV

CMD 设置环境变量

查看所有环境变量

1
set

查看单个环境变量

1
set NODE_ENV

添加/更新环境变量

1
set NODE_ENV=development

删除环境变量

1
set NODE_ENV=

Linux 系统

Linux 下使用 envset 命令查看环境变量

这个文章不错 https://www.cnblogs.com/youyoui/p/10680329.html

搜集几篇相关文章和好使的代码块

文章

Go exec command

Advanced command execution in Go with os/exec edit

go os/exec 简明教程

代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"log"
"os/exec"
)

func main() {
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
1
2
3
4
5
6
7
8
9
cmd := exec.Command("date")
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Println("out:", outb.String(), "err:", errb.String())
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
26
package main

import (
"fmt"
"os/exec"
)

func main() {
app := "echo"

arg0 := "-e"
arg1 := "Hello world"
arg2 := "\n\tfrom"
arg3 := "golang"

cmd := exec.Command(app, arg0, arg1, arg2, arg3)
stdout, err := cmd.Output()

if err != nil {
fmt.Println(err.Error())
return
}

// Print the output
fmt.Println(string(stdout))
}
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
26
27
28
29
30
31
package main

import (
"bytes"
"fmt"
"log"
"os/exec"
)

const ShellToUse = "bash"

func Shellout(command string) (error, string, string) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(ShellToUse, "-c", command)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
return err, stdout.String(), stderr.String()
}

func main() {
err, out, errout := Shellout("ls -ltr")
if err != nil {
log.Printf("error: %v\n", err)
}
fmt.Println("--- stdout ---")
fmt.Println(out)
fmt.Println("--- stderr ---")
fmt.Println(errout)
}

网上找了一下,没有找到相关文档,但是文章是有的

Building Go Applications for Different Operating Systems and Architectures

How To Build Go Executables for Multiple Platforms on Ubuntu 16.04

Go 条件编译

Go (Golang) GOOS and GOARCH

Golang交叉编译各个平台的二进制文件

Golang 支持在一个平台下生成另一个平台可执行程序的交叉编译功能。

Mac下编译Linux, Windows平台的64位可执行程序:

1
2
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build test.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build test.go

Linux下编译Mac, Windows平台的64位可执行程序:

1
2
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build test.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build test.go

Windows下编译Mac, Linux平台的64位可执行程序,注意一下用CMD

1
2
3
4
5
6
7
8
9
10
11
12
13
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build test.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build test.go

SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build main.go

SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build main.go

GOOS:目标可执行程序运行操作系统,支持 darwin,freebsd,linux,windows GOARCH:目标可执行程序操作系统构架,包括 386,amd64,arm

转载自:https://blog.csdn.net/panshiqu/article/details/53788067

问题

vue 的 style 加入 scoped 属性的时候,会给相关 dom 加上类似这样的东西 [data-v-26725ac2],导致你可能没选中某些用 js 生成的 dom,后来我用v-deep这种方式解决了,这是一个比较巧妙的解决方式。

1
2
3
4
.dropdown-menu[data-v-26725ac2] {
margin-top: 5px;
width: 117px;
}

我当时在群里问 [data-v-26725ac2] 是什么,这个叫Attr,也叫属性选择器,感谢群友蕾姆司机的提示。

解决

1
2
3
4
5
6
7
8
9
.dropdown-menu {
margin-top: 5px;
width: 117px;
::v-deep {
.popper__arrow {
display: none;
}
}
}

Go 语言教程我看了个大概,想写一个监控网站的小程序,代码如下。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main

import (
"fmt"
"github.com/go-resty/resty/v2"
)

func main() {
// Create a Resty Client
client := resty.New()

resp, err := client.R().
EnableTrace().
Get("https://www.baidu.com")

// Explore response object
fmt.Println("Response Info:")
fmt.Println(" Error :", err)
fmt.Println(" Status Code:", resp.StatusCode())
fmt.Println(" Status :", resp.Status())
fmt.Println(" Proto :", resp.Proto())
fmt.Println(" Time :", resp.Time())
fmt.Println(" Received At:", resp.ReceivedAt())
fmt.Println(" Body :\n", resp)
fmt.Println()

// Explore trace info
fmt.Println("Request Trace Info:")
ti := resp.Request.TraceInfo()
fmt.Println(" DNSLookup :", ti.DNSLookup)
fmt.Println(" ConnTime :", ti.ConnTime)
fmt.Println(" TCPConnTime :", ti.TCPConnTime)
fmt.Println(" TLSHandshake :", ti.TLSHandshake)
fmt.Println(" ServerTime :", ti.ServerTime)
fmt.Println(" ResponseTime :", ti.ResponseTime)
fmt.Println(" TotalTime :", ti.TotalTime)
fmt.Println(" IsConnReused :", ti.IsConnReused)
fmt.Println(" IsConnWasIdle :", ti.IsConnWasIdle)
fmt.Println(" ConnIdleTime :", ti.ConnIdleTime)
fmt.Println(" RequestAttempt:", ti.RequestAttempt)
fmt.Println(" RemoteAddr :", ti.RemoteAddr.String())
}

但是我发现,去请求大文件的时候,程序响应很慢,而我只需要返回成功的状态码就行了。

研究了一下,发现可以设置 HeaderRange"bytes=0-1023",限制请求数据大小。因为我只关心状态码而不关心 body 内容

MDN - https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Range

改了一下,代码如下

1
2
3
4
resp, err := client.R().
EnableTrace().
SetHeader("Range", "bytes=0-1023").
Get("https://www.baidu.com")