[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! ~~~