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!