简体   繁体   中英

Go exec.Command pipe output into another powershell

I'm new to Go and I've the following problem. I need to use os.exec to interact with the powershell and capture the output per command/pipe in it.

Eg I have the following command

powershell /C cat somefile.md | Select-String -Pattern someinput

I need the output of the first command

powershell /C cat somefile.md

and the output of the pipe

powershell /C Select-String -Pattern someinput

The code below works fine on linux and with cmd on Windows, I have to use powershell tho...

Edit: After further investigation I came to the conclusion that Select-String and other powershell specific commands are just not pipe-able, for whatever reason. Is it because powershell needs an PSObject as input?

That's my messy code right now:

func executeCmds{
    //save all exec.Commands in one array (I've no clue how many cmds there are later)
    var cmdList []exec.Cmds 

    cmdList = append(cmdList, *exec.Command("powershell", "/C", "cat somefile.md"))

    //*****EDIT*****//

    // not working $Input is missing
    cmdList = append(cmdList, *exec.Command("powershell", "/C", "Select-String -Pattern someinput"))

    // working command $Input is important
    cmdList = append(cmdList, exec.Command("powershell", "/C", "$Input | Select-String -Pattern someinput"))

    // this is performance wise NOT an option!
    // cmdList = append(cmdList, *exec.Command("powershell", "/C", "cat somefile.md | Select-String -Pattern someinput"))
    
    // save the latest stdout into tmp to stdin the next command (works fine on linux and cmd.exe)
    var tmp []byte

    for i, s := range cmdList {
        if i > 0 {
            s.Stdin = strings.NewReader(string(tmp)) //this does not work, powershell somehow can't use Stdin
        }

        stdout, err := s.CombinedOutput()
        tmp = stdout

        if err != nil {
            o := fmt.Sprint(err) + ": " + string(stdout)
            err = errors.New(o)
            panic(err)
            break
        }

        // Print the output
        fmt.Println(string(stdout))
    }
}

You dont need to use PowerShell for this, you can just use Go directly:

package main
import "golang.org/x/sys/windows/registry"

func main() {
   a, e := registry.CURRENT_USER.ReadSubKeyNames(0)
   if e != nil {
      panic(e)
   }
   for _, s := range a {
      println(s)
   }
}

https://pkg.go.dev/golang.org/x/sys/windows/registry

If you need to pass the data through a go application then you can do something like the following (I'm not sure how this will improve performance over using the powershell pipeline):

package main

import (
    "fmt"
    "io"
    "os/exec"
)

func main() {
    // save all exec.Commands in one array (I've no clue how many cmds there are later)
    var cmdList []*exec.Cmd

    cmdList = append(cmdList, exec.Command("powershell", "/C", "cat somefile.md"))
    cmdList = append(cmdList, exec.Command("powershell", "/C", "$Input | Select-String -Pattern someinput"))

    var tmp []byte
    for i, s := range cmdList {
        if i > 0 { // Send the output of the previous command into the next command
            input, err := s.StdinPipe()
            if err != nil {
                panic(err)
            }
            go func(w io.WriteCloser, data []byte) {
                w.Write(data) // Should really check for errors here
                w.Close()
            }(input, tmp)
        }
        var err error
        tmp, err = s.CombinedOutput() // As you are taking standard error here too you should check for errors
        if err != nil {
            panic(err)
        }
    }
    // Print the final output
    fmt.Println(string(tmp))
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM