Метод exec.Cmd.StderrPipe

Метод exec.Cmd.StderrPipe создает канал (pipe), который подключается к стандартному потоку ошибок (STDERR) команды. Он возвращает io.ReadCloser, из которого можно читать вывод ошибок команды. Этот метод полезен, когда нужно отдельно обрабатывать ошибочный вывод команды, не смешивая его с обычным выводом (STDOUT).

Важно: канал должен быть прочитан полностью до вызова exec.Cmd.Run или exec.Cmd.Wait, иначе команда может заблокироваться при записи в STDERR. Обычно чтение из канала выполняют в отдельной горутине или читают весь вывод через io.ReadAll.

Синтаксис

gofunc (c *Cmd) StderrPipe() (io.ReadCloser, error)

Пример

Давайте выполним команду, которая всегда завершается с ошибкой, и прочитаем её STDERR:

gopackage main

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

func main() {
  cmd := exec.Command("ls", "--invalid-flag")

  stderr, err := cmd.StderrPipe()
  if err != nil {
    fmt.Println("Error creating pipe:", err)
    return
  }

  err = cmd.Start()
  if err != nil {
    fmt.Println("Error starting command:", err)
    return
  }

  var errBuf bytes.Buffer
  _, err = errBuf.ReadFrom(stderr)
  if err != nil {
    fmt.Println("Error reading stderr:", err)
    return
  }

  err = cmd.Wait()
  if err != nil {
    fmt.Println("Command finished with error:", err)
  }

  fmt.Println("STDERR output:", errBuf.String())
}

Результат выполнения кода:

go"Command finished with error: exit status 2"
"STDERR output: ls: unrecognized option '--invalid-flag'"

Пример

Теперь рассмотрим пример, где мы читаем STDERR и STDOUT одновременно из одной команды:

gopackage main

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

func main() {
  cmd := exec.Command("sh", "-c", "echo 'stdout text' && echo 'stderr text' >&2")

  stdout, err := cmd.StdoutPipe()
  if err != nil {
    fmt.Println("Error creating stdout pipe:", err)
    return
  }

  stderr, err := cmd.StderrPipe()
  if err != nil {
    fmt.Println("Error creating stderr pipe:", err)
    return
  }

  err = cmd.Start()
  if err != nil {
    fmt.Println("Error starting command:", err)
    return
  }

  go func() {
    scanner := bufio.NewScanner(stdout)
    for scanner.Scan() {
      fmt.Println("STDOUT:", scanner.Text())
    }
  }()

  scanner := bufio.NewScanner(stderr)
  for scanner.Scan() {
    fmt.Println("STDERR:", scanner.Text())
  }

  err = cmd.Wait()
  if err != nil {
    fmt.Println("Command finished with error:", err)
  }
}

Результат выполнения кода:

go"STDERR: stderr text"
"STDOUT: stdout text"

Пример

Если нужно прочитать STDERR как строку, можно использовать функцию io.ReadAll:

gopackage main

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

func main() {
  cmd := exec.Command("go", "mod", "tidy")

  stderr, err := cmd.StderrPipe()
  if err != nil {
    fmt.Println("Error creating pipe:", err)
    return
  }

  err = cmd.Start()
  if err != nil {
    fmt.Println("Error starting command:", err)
    return
  }

  errData, err := io.ReadAll(stderr)
  if err != nil {
    fmt.Println("Error reading stderr:", err)
    return
  }

  err = cmd.Wait()
  if err != nil {
    fmt.Println("Command finished with error:", err)
  }

  if len(errData) > 0 {
    fmt.Println("STDERR:", string(errData))
  } else {
    fmt.Println("No stderr output")
  }
}

Результат выполнения кода:

go"No stderr output"

Смотрите также

  • метод exec.Cmd.StdinPipe,
    который создает канал для записи в STDIN команды
  • метод exec.Cmd.StdoutPipe,
    который создает канал для чтения STDOUT команды
  • метод exec.Cmd.Output,
    который возвращает STDOUT команды как байтовый срез
  • метод exec.Cmd.CombinedOutput,
    который возвращает объединенный вывод STDOUT и STDERR