golang中的shell

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!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: 你可以使用 `os/exec` 包来在 Go 实现与 Shell 的交互。下面是一个简单的示例代码: ```go package main import ( "bufio" "fmt" "os/exec" ) func main() { cmd := exec.Command("sh") stdin, err := cmd.StdinPipe() if err != nil { panic(err) } stdout, err := cmd.StdoutPipe() if err != nil { panic(err) } scanner := bufio.NewScanner(stdout) if err := cmd.Start(); err != nil { panic(err) } go func() { defer stdin.Close() stdin.Write([]byte("echo hello world\n")) }() for scanner.Scan() { fmt.Println(scanner.Text()) } if err := cmd.Wait(); err != nil { panic(err) } } ``` 这个例子创建了一个 `sh` 进程,并通过管道向其输入了一个命令 `echo hello world`,然后读取了输出并打印到控制台。你可以根据自己的需求修改这个例子。 ### 回答2: golangShell交互是指在Golang程序执行Shell命令并与其进行交互的功能。 在Golang实现Shell交互可以使用os/exec包。该包提供了执行Shell命令的函数和方法,可以通过它们来执行Shell命令并获取命令的标准输出、标准错误和执行结果。 首先,我们需要引入os/exec包。然后,使用exec.Command函数创建一个Cmd类型的对象,该对象表示要执行的Shell命令。可以通过Cmd对象的方法设置命令的参数、工作目录等。 接下来,使用Cmd对象的CombinedOutput方法来执行Shell命令,并获取命令的输出结果。CombinedOutput方法会返回一个字节数组,其存储了命令的标准输出和标准错误。 最后,我们可以将字节数组转换为字符串,并进行相应的处理,如打印输出或进一步解析。 以下是一个简单的示例代码,演示了如何在Golang实现Shell交互的功能: ``` package main import ( "fmt" "log" "os/exec" ) func main() { cmd := exec.Command("ls", "-l") // 创建Cmd对象,执行ls -l命令 output, err := cmd.CombinedOutput() // 执行命令并获取输出结果 if err != nil { log.Fatal(err) // 如果执行命令出错,打印错误信息并退出 } fmt.Println(string(output)) // 将字节数组转换为字符串并打印输出 } ``` 以上代码会执行`ls -l`命令,并将结果打印到控制台。 需要注意的是,在使用Golang执行Shell命令时,需要保证可执行的命令位于环境变量`$PATH`所指定的路径,否则需要指定完整的命令路径。示例的`ls`命令是一个常见的Shell命令,因此不需要指定路径。 总结来说,通过使用os/exec包,我们可以在Golang程序实现Shell交互的功能,从而执行Shell命令,并获取其输出结果。 ### 回答3: Go语言本身并不提供直接的Shell交互功能,但我们可以使用一些第三方库来实现类似的功能。 一个常用的库是“os/exec”,它允许我们执行外部 Shell 命令并与其进行交互。下面是一个简单的示例代码: ```go package main import ( "bufio" "fmt" "os" "os/exec" ) func main() { // 创建一个命令对象 cmd := exec.Command("/bin/sh") // 获取命令的标准输入和输出管道 stdin, _ := cmd.StdinPipe() stdout, _ := cmd.StdoutPipe() // 启动命令 cmd.Start() // 准备一个输入读取器,用于读取用户输入 reader := bufio.NewReader(os.Stdin) for { // 读取用户输入 input, _ := reader.ReadString('\n') // 将用户输入写入命令的标准输入管道 stdin.Write([]byte(input)) // 读取命令的标准输出 output := make([]byte, 1024) stdout.Read(output) // 打印输出结果 fmt.Println(string(output)) } } ``` 这段代码会启动一个新的Shell,并不断读取用户的输入并将其发送给Shell的标准输入,然后读取Shell的标准输出并打印出来。 请注意,这里的示例代码没有处理错误情况,为了简单起见,省略了错误处理部分。在实际应用,我们应该对错误进行适当处理。 希望这个示例能够帮助你实现Go语言下的Shell交互功能。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盼盼编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值