[TOC]
# api
~~~
func (*Cmd) Run
func (c *Cmd) Run() error
Run执行c包含的命令,并阻塞直到完成。
如果命令成功执行,stdin、stdout、stderr的转交没有问题,并且返回状态码为0,方法的返回值为nil;如果命令没有执行或者执行失败,会返回*ExitError类型的错误;否则返回的error可能是表示I/O问题。
~~~
~~~
func (*Cmd) Start
func (c *Cmd) Start() error
Start开始执行c包含的命令,但并不会等待该命令完成即返回。Wait方法会返回命令的返回状态码并在命令返回后释放相关的资源。
~~~
~~~
func (*Cmd) Wait
func (c *Cmd) Wait() error
Wait会阻塞直到该命令执行完成,该命令必须是被Start方法开始执行的。
如果命令成功执行,stdin、stdout、stderr的转交没有问题,并且返回状态码为0,方法的返回值为nil;如果命令没有执行或者执行失败,会返回*ExitError类型的错误;否则返回的error可能是表示I/O问题。Wait方法会在命令返回后释放相关的资源。
~~~
~~~
func (*Cmd) Output
func (c *Cmd) Output() ([]byte, error)
执行命令并返回标准输出的切片。
~~~
~~~
func (*Cmd) StderrPipe
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe方法返回一个在命令Start后与命令标准错误输出关联的管道。Wait方法获知命令结束后会关闭这个管道,一般不需要显式的关闭该管道。但是在从管道读取完全部数据之前调用Wait是错误的;同样使用StderrPipe方法时调用Run函数也是错误的。
~~~
# 阻塞方式(需要执行结果)
主要用于执行shell命令,并且返回shell的标准输出
## 适用于执行普通非阻塞shell命令,且需要shell标准输出的
~~~
//阻塞式的执行外部shell命令的函数,等待执行完毕并返回标准输出
func exec_shell(s string) (string, error){
//函数返回一个*Cmd,用于使用给出的参数执行name指定的程序
cmd := exec.Command("/bin/bash", "-c", s)
//读取io.Writer类型的cmd.Stdout,再通过bytes.Buffer(缓冲byte类型的缓冲器)将byte类型转化为string类型(out.String():这是bytes类型提供的接口)
var out bytes.Buffer
cmd.Stdout = &out
//Run执行c包含的命令,并阻塞直到完成。 这里stdout被取出,cmd.Wait()无法正确获取stdin,stdout,stderr,则阻塞在那了
err := cmd.Run()
checkErr(err)
return out.String(), err
}
~~~
## 需要对shell标准输出的**逐行实时**进行处理的
~~~
func execCommand(commandName string, params []string) bool {
//函数返回一个*Cmd,用于使用给出的参数执行name指定的程序
cmd := exec.Command(commandName, params...)
//显示运行的命令
fmt.Println(cmd.Args)
//StdoutPipe方法返回一个在命令Start后与命令标准输出关联的管道。Wait方法获知命令结束后会关闭这个管道,一般不需要显式的关闭该管道。
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
return false
}
cmd.Start()
//创建一个流来读取管道内内容,这里逻辑是通过一行一行的读取的
reader := bufio.NewReader(stdout)
//实时循环读取输出流中的一行内容
for {
line, err2 := reader.ReadString('\n')
if err2 != nil || io.EOF == err2 {
break
}
fmt.Println(line)
}
//阻塞直到该命令执行完成,该命令必须是被Start方法开始执行的
cmd.Wait()
return true
}
~~~
# 非阻塞方式(不需要执行结果)
通过shell调用自己的程序,并且程序是死循环,此时无法获取返回结果(否则程序会一直阻塞直至调用的 程序结束)
**适用于调用自己写的程序(服务器死循环,且不需要返回结果的)**
~~~
//不需要执行命令的结果与成功与否,执行命令马上就返回
func exec_shell_no_result(command string) {
//处理启动参数,通过空格分离 如:setsid /home/luojing/gotest/src/test_main/iwatch/test/while_little &
command_name_and_args := strings.FieldsFunc(command, splite_command)
//开始执行c包含的命令,但并不会等待该命令完成即返回
cmd.Start()
if err != nil {
fmt.Printf("%v: exec command:%v error:%v\n", get_time(), command, err)
}
fmt.Printf("Waiting for command:%v to finish...\n", command)
//阻塞等待fork出的子进程执行的结果,和cmd.Start()配合使用[不等待回收资源,会导致fork出执行shell命令的子进程变为僵尸进程]
err = cmd.Wait()
if err != nil {
fmt.Printf("%v: Command finished with error: %v\n", get_time(), err)
}
return
}
~~~
~~~
/错误处理函数
func checkErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}
~~~
# ssh
~~~
go get golang.org/x/crypto/ssh
~~~
## ssh执行命令
这个方法需要有一个环境的准备:与目标服务器建立免密码登陆],并且执行程序的用户与执行用户一致
~~~
import (
"net"
"log"
"fmt"
"bytes"
"os/exec"
"strconv"
str "strings"
"golang.org/x/crypto/ssh"
)
func runCmd(){
var stdOut, stdErr bytes.Buffer
cmd := exec.Command( "ssh", "username@192.168.1.4", "if [ -d liujx/project ];then echo 0;else echo 1;fi" )
cmd.Stdout = &stdOut
cmd.Stderr = &stdErr
if err := cmd.Run(); err != nil {
fmt.Printf( "cmd exec failed: %s : %s", fmt.Sprint( err ), stdErr.String() )
}
fmt.Print( stdOut.String() )
ret, err := strconv.Atoi( str.Replace( stdOut.String(), "\n", "", -1 ) )
if err != nil {
panic(err)
}
fmt.Printf("%d, %s\n", ret, stdErr.String() )
}
~~~
## ssh客户端连接
这种方法可以不用搭建免密码登陆环境,连接时可指定用户和密码的
~~~
func SSHConnect( user, password, host string, port int ) ( *ssh.Session, error ) {
var (
auth []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
client *ssh.Client
session *ssh.Session
err error
)
// get auth method
auth = make([]ssh.AuthMethod, 0)
auth = append(auth, ssh.Password(password))
hostKeyCallbk := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
}
clientConfig = &ssh.ClientConfig{
User: user,
Auth: auth,
// Timeout: 30 * time.Second,
HostKeyCallback: hostKeyCallbk,
}
// connet to ssh
addr = fmt.Sprintf( "%s:%d", host, port )
if client, err = ssh.Dial( "tcp", addr, clientConfig ); err != nil {
return nil, err
}
// create session
if session, err = client.NewSession(); err != nil {
return nil, err
}
return session, nil
}
func runSsh(){
var stdOut, stdErr bytes.Buffer
session, err := SSHConnect( "username", "passworld", "192.168.1.4", 22 )
if err != nil {
log.Fatal(err)
}
defer session.Close()
session.Stdout = &stdOut
session.Stderr = &stdErr
session.Run("if [ -d liujx/project ]; then echo 0; else echo 1; fi")
ret, err := strconv.Atoi( str.Replace( stdOut.String(), "\n", "", -1 ) )
if err != nil {
panic(err)
}
fmt.Printf("%d, %s\n", ret, stdErr.String() )
}
~~~
# os.Args
~~~
func main() {
osArg := os.Args //[]string
/**
o 开始,第一个是文件路径
*/
for i, data := range osArg {
fmt.Println(i, data)
}
}
~~~
# flag命令行参数
每个处理一行
~~~
func main() {
var recusive bool
var test string
var level int
flag.BoolVar(&recusive, "r", false, "recusive xxx")
flag.StringVar(&test, "t","default string", "string option")
flag.IntVar(&level, "l", 1, "level of xxx")
flag.Parse()
fmt.Println(recusive)
fmt.Println(test)
fmt.Println(level)
}
~~~
不传就给默认值
~~~
➜ studygo ./studygo
false
default string
1
~~~
传递就按照传递的来
~~~
➜ studygo ./studygo -r -t hello -l 11
true
hello
11
~~~
# urfave/cli框架
可以通过-h查看帮助
~~~
import (
"fmt"
"github.com/urfave/cli"
"os"
)
func main() {
var language string
var recusive bool
app := cli.NewApp()
//指定名字
app.Name = "greet"
app.Usage = "用法"
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "select language",
Destination: &language,
},
cli.BoolFlag{
Name: "recusive, r",
Usage: "recusive for the greeting",
Destination: &recusive,
},
}
app.Action = func(c *cli.Context) error {
var cmd string
//如果用户传过来 >0
if c.NArg() > 0 {
cmd = c.Args()[0]
fmt.Println("cmd is ", cmd)
}
fmt.Println("recusive is: ", recusive)
fmt.Println("language is: ", language)
return nil
}
app.Run(os.Args)
}
~~~
# 启动外部命令和程序
os 包有一个`StartProcess`函数可以调用或启动外部系统命令和二进制可执行文件;它的第一个参数是要运行的进程,第二个参数用来传递选项或参数,第三个参数是含有系统环境基本信息的结构体。
这个函数返回被启动进程的 id(pid),或者启动失败返回错误。
exec 包中也有同样功能的更简单的结构体和函数;主要是`exec.Command(name string, arg ...string)`和`Run()`。首先需要用系统命令或可执行文件的名字创建一个`Command`对象,然后用这个对象作为接收者调用`Run()`。下面的程序(因为是执行 Linux 命令,只能在 Linux 下面运行)演示了它们的使用:
~~~
// exec.go
package main
import (
"fmt"
"os/exec"
"os"
)
func main() {
// 1) os.StartProcess //
/*********************/
/* Linux: */
env := os.Environ()
procAttr := &os.ProcAttr{
Env: env,
Files: []*os.File{
os.Stdin,
os.Stdout,
os.Stderr,
},
}
// 1st example: list files
pid, err := os.StartProcess("/bin/ls", []string{"ls", "-l"}, procAttr)
if err != nil {
fmt.Printf("Error %v starting process!", err) //
os.Exit(1)
}
fmt.Printf("The process id is %v", pid)
~~~
输出:
~~~
The process id is &{2054 0}total 2056
-rwxr-xr-x 1 ivo ivo 1157555 2011-07-04 16:48 Mieken_exec
-rw-r--r-- 1 ivo ivo 2124 2011-07-04 16:48 Mieken_exec.go
-rw-r--r-- 1 ivo ivo 18528 2011-07-04 16:48 Mieken_exec_go_.6
-rwxr-xr-x 1 ivo ivo 913920 2011-06-03 16:13 panic.exe
-rw-r--r-- 1 ivo ivo 180 2011-04-11 20:39 panic.go
~~~
~~~
// 2nd example: show all processes
pid, err = os.StartProcess("/bin/ps", []string{"-e", "-opid,ppid,comm"}, procAttr)
if err != nil {
fmt.Printf("Error %v starting process!", err) //
os.Exit(1)
}
fmt.Printf("The process id is %v", pid)
~~~
~~~
// 2) exec.Run //
/***************/
// Linux: OK, but not for ls ?
// cmd := exec.Command("ls", "-l") // no error, but doesn't show anything ?
// cmd := exec.Command("ls") // no error, but doesn't show anything ?
cmd := exec.Command("gedit") // this opens a gedit-window
err = cmd.Run()
if err != nil {
fmt.Printf("Error %v executing command!", err)
os.Exit(1)
}
fmt.Printf("The command is %v", cmd)
// The command is &{/bin/ls [ls -l] [] <nil> <nil> <nil> 0xf840000210 <nil> true [0xf84000ea50 0xf84000e9f0 0xf84000e9c0] [0xf84000ea50 0xf84000e9f0 0xf84000e9c0] [] [] 0xf8400128c0}
}
// in Windows: uitvoering: Error fork/exec /bin/ls: The system cannot find the path specified. starting process!
~~~
- 基础
- 简介
- 主要特征
- 变量和常量
- 编码转换
- 数组
- byte与rune
- big
- sort接口
- 和mysql类型对应
- 函数
- 闭包
- 工作区
- 复合类型
- 指针
- 切片
- map
- 结构体
- sync.Map
- 随机数
- 面向对象
- 匿名组合
- 方法
- 接口
- 权限
- 类型查询
- 异常处理
- error
- panic
- recover
- 自定义错误
- 字符串处理
- 正则表达式
- json
- 文件操作
- os
- 文件读写
- 目录
- bufio
- ioutil
- gob
- 栈帧的内存布局
- shell
- 时间处理
- time详情
- time使用
- new和make的区别
- container
- list
- heap
- ring
- 测试
- 单元测试
- Mock依赖
- delve
- 命令
- TestMain
- path和filepath包
- log日志
- 反射
- 详解
- plugin包
- 信号
- goto
- 协程
- 简介
- 创建
- 协程退出
- runtime
- channel
- select
- 死锁
- 互斥锁
- 读写锁
- 条件变量
- 嵌套
- 计算单个协程占用内存
- 执行规则
- 原子操作
- WaitGroup
- 定时器
- 对象池
- sync.once
- 网络编程
- 分层模型
- socket
- tcp
- udp
- 服务端
- 客户端
- 并发服务器
- Http
- 简介
- http服务器
- http客户端
- 爬虫
- 平滑重启
- context
- httptest
- 优雅中止
- web服务平滑重启
- beego
- 安装
- 路由器
- orm
- 单表增删改查
- 多级表
- orm使用
- 高级查询
- 关系查询
- SQL查询
- 元数据二次定义
- 控制器
- 参数解析
- 过滤器
- 数据输出
- 表单数据验证
- 错误处理
- 日志
- 模块
- cache
- task
- 调试模块
- config
- 部署
- 一些包
- gjson
- goredis
- collection
- sjson
- redigo
- aliyunoss
- 密码
- 对称加密
- 非对称加密
- 单向散列函数
- 消息认证
- 数字签名
- mysql优化
- 常见错误
- go run的错误
- 新手常见错误
- 中级错误
- 高级错误
- 常用工具
- 协程-泄露
- go env
- gometalinter代码检查
- go build
- go clean
- go test
- 包管理器
- go mod
- gopm
- go fmt
- pprof
- 提高编译
- go get
- 代理
- 其他的知识
- go内存对齐
- 细节总结
- nginx路由匹配
- 一些博客
- redis为什么快
- cpu高速缓存
- 常用命令
- Go 永久阻塞的方法
- 常用技巧
- 密码加密解密
- for 循环迭代变量
- 备注
- 垃圾回收
- 协程和纤程
- tar-gz
- 红包算法
- 解决golang.org/x 下载失败
- 逃逸分析
- docker
- 镜像
- 容器
- 数据卷
- 网络管理
- 网络模式
- dockerfile
- docker-composer
- 微服务
- protoBuf
- GRPC
- tls
- consul
- micro
- crontab
- shell调用
- gorhill/cronexpr
- raft
- go操作etcd
- mongodb