[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