Функция exec.CommandContext

Функция exec.CommandContext создает новый экземпляр структуры exec.Cmd для выполнения внешней команды, но в отличие от exec.Command, она принимает первым параметром контекст context.Context, который позволяет управлять временем жизни процесса. Вторым параметром передается путь к исполняемому файлу или имя команды, а последующие параметры представляют аргументы командной строки.

Синтаксис

goexec.CommandContext(ctx, name, arg...)

Функция возвращает указатель на структуру exec.Cmd, которую можно настроить и запустить. В случае, если переданный контекст будет отменен или истечет его таймаут, запущенный процесс получит сигнал SIGKILL и завершится.

Пример - простой запуск команды

Давайте выполним команду echo с текстом "hello world" и получим ее вывод:

gopackage main

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

func main() {
  ctx := context.Background()
  cmd := exec.CommandContext(ctx, "echo", "hello world")

  output, err := cmd.Output()
  if err != nil {
    fmt.Println("Error:", err)
    return
  }

  fmt.Println(string(output))
}

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

go"hello world\n"

Пример - установка таймаута

Давайте запустим команду sleep на 5 секунд, но установим таймаут в 2 секунды:

gopackage main

import (
  "context"
  "fmt"
  "os/exec"
  "time"
)

func main() {
  ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  defer cancel()

  cmd := exec.CommandContext(ctx, "sleep", "5")

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

  fmt.Println("Command finished successfully")
}

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

go"Error: signal: killed"

Пример - ручная отмена команды

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

gopackage main

import (
  "context"
  "fmt"
  "os/exec"
  "time"
)

func main() {
  ctx, cancel := context.WithCancel(context.Background())

  go func() {
    time.Sleep(1 * time.Second)
    cancel()
  }()

  cmd := exec.CommandContext(ctx, "sleep", "10")

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

  fmt.Println("Command finished successfully")
}

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

go"Error: signal: killed"

Пример - получение вывода и ошибок

Давайте выполним команду и обработаем как стандартный вывод, так и стандартный поток ошибок:

gopackage main

import (
  "context"
  "fmt"
  "os/exec"
  "time"
)

func main() {
  ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
  defer cancel()

  cmd := exec.CommandContext(ctx, "sh", "-c", "echo stdout; echo stderr >&2")

  stdout, err := cmd.Output()
  if err != nil {
    if exitErr, ok := err.(*exec.ExitError); ok {
      fmt.Println("Stderr:", string(exitErr.Stderr))
    }
    fmt.Println("Error:", err)
    return
  }

  fmt.Println("Stdout:", string(stdout))
}

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

go"Stdout: stdout\n"

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

  • функцию exec.Command,
    которая создает команду без контекста
  • функцию exec.Cmd.Run,
    которая запускает команду и ожидает ее завершения
  • функцию exec.Cmd.Output,
    которая запускает команду и возвращает стандартный вывод
  • функцию exec.ExitError,
    которая представляет ошибку завершения команды с ненулевым кодом