Пакет exec
Обзор
Пакет exec запускает внешние команды. Он оборачивает os.StartProcess, упрощая переназначение stdin и stdout, соединение ввода-вывода с помощью каналов и выполнение других настроек.
В отличие от системного вызова библиотеки «system» из C и других языков, пакет os/exec намеренно не вызывает системную оболочку и не раскрывает шаблоны glob, а также не обрабатывает другие подстановки, конвейеры или перенаправления, которые обычно выполняют оболочки. Пакет работает скорее как семейство функций «exec» в C. Чтобы раскрыть шаблоны glob, вызовите оболочку напрямую, позаботившись об экранировании потенциально опасных входных данных, или используйте функцию Glob пакета path/filepath. Для раскрытия переменных окружения используйте ExpandEnv пакета os.
Обратите внимание, что примеры в этом пакете рассчитаны на Unix-систему. Они могут не работать в Windows и не работают в Go Playground, используемом go.dev и pkg.go.dev.
Исполняемые файлы в текущем каталоге
Функции Command и LookPath ищут программу в каталогах, перечисленных в текущем пути, следуя соглашениям операционной системы. На протяжении десятилетий операционные системы включали текущий каталог в этот поиск — иногда неявно, а иногда по умолчанию в соответствии с явной настройкой. Современная практика показывает, что включение текущего каталога обычно оказывается неожиданным и часто приводит к проблемам безопасности.
Чтобы избежать этих проблем безопасности, начиная с Go 1.19 этот пакет не разрешает путь к программе, если он содержит неявную или явную запись пути относительно текущего каталога. То есть вызов LookPath("go") не вернёт успешно ./go в Unix или .\go.exe в Windows независимо от конфигурации пути. Вместо этого, если обычные алгоритмы поиска пути дали бы такой результат, эти функции возвращают ошибку err, для которой errors.Is(err, ErrDot) возвращает true.
Например, рассмотрим следующие два фрагмента программы:
path, err := exec.LookPath("prog")
if err != nil {
log.Fatal(err)
}
use(path)
и
cmd := exec.Command("prog")
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
Они не найдут и не запустят ./prog или .\prog.exe независимо от конфигурации текущего пути.
Код, которому всегда нужно запускать программу из текущего каталога, можно переписать, указав "./prog" вместо "prog".
Код, которому необходимо учитывать результаты поиска в относительных записях пути, может вместо этого переопределить ошибку, проверив её с помощью errors.Is:
path, err := exec.LookPath("prog")
if errors.Is(err, exec.ErrDot) {
err = nil
}
if err != nil {
log.Fatal(err)
}
use(path)
и
cmd := exec.Command("prog")
if errors.Is(cmd.Err, exec.ErrDot) {
cmd.Err = nil
}
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
Установка переменной окружения GODEBUG=execerrdot=0 полностью отключает генерацию ErrDot, временно восстанавливая поведение до Go 1.19 для программ, которые не могут применить более точечные исправления. В будущей версии Go поддержка этой переменной может быть удалена.
Прежде чем добавлять такие переопределения, убедитесь, что понимаете связанные с этим последствия для безопасности. Дополнительные сведения см. на странице https://go.dev/blog/path-security.
Указатель
Примеры
Файлы пакета
exec.go exec_unix.go lookpath.go lp_unix.go
Переменные
ErrDot указывает, что поиск пути разрешился в исполняемый файл в текущем каталоге из-за наличия «.» в пути — неявно или явно. Подробности см. в документации пакета.
Обратите внимание, что функции этого пакета не возвращают ErrDot напрямую. Чтобы проверить, связана ли возвращённая ошибка err с этим условием, следует использовать errors.Is(err, ErrDot), а не err == ErrDot.
var ErrDot = errors.New("cannot run executable found relative to current directory") ErrNotFound — ошибка, возникающая, если при поиске по пути не удалось найти исполняемый файл.
var ErrNotFound = errors.New("executable file not found in $PATH") Cmd.Wait возвращает ErrWaitDelay, если процесс завершается с успешным кодом состояния, но каналы вывода не закрываются до истечения WaitDelay команды.
var ErrWaitDelay = errors.New("exec: WaitDelay expired before I/O complete") func LookPath
func LookPath(file string) (string, error)
LookPath ищет исполняемый файл с именем file в текущем пути, следуя соглашениям операционной системы. Если file содержит косую черту, поиск выполняется непосредственно по этому имени, а путь по умолчанию не используется. В противном случае при успехе возвращается абсолютный путь.
LookPath возвращает ошибку, для которой errors.Is(err, ErrDot) возвращает true, если разрешённый путь является относительным к текущему каталогу. Дополнительные сведения см. в документации пакета.
LookPath ищет исполняемый файл с именем file в каталогах, указанных переменной окружения PATH, за исключением описанных ниже случаев.
- В Windows файл должен иметь расширение, указанное переменной окружения PATHEXT. Если PATHEXT не задана, файл должен иметь расширение ".com", ".exe", ".bat" или ".cmd".
- В Plan 9 LookPath использует переменную окружения path. Если file начинается с "/", "#", "./" или "../", поиск выполняется непосредственно по этому имени, а path не используется.
- В Wasm LookPath всегда возвращает ошибку.
Пример
Код:
path, err := exec.LookPath("fortune")
if err != nil {
log.Fatal("installing fortune is in your future")
}
fmt.Printf("fortune is available at %s\n", path)
type Cmd
Cmd представляет внешнюю команду, подготовленную к запуску или уже выполняемую.
После вызова методов Cmd.Start, Cmd.Run, Cmd.Output или Cmd.CombinedOutput объект Cmd нельзя использовать повторно.
type Cmd struct {
// Path is the path of the command to run.
//
// This is the only field that must be set to a non-zero
// value. If Path is relative, it is evaluated relative
// to Dir.
Path string
// Args holds command line arguments, including the command as Args[0].
// If the Args field is empty or nil, Run uses {Path}.
//
// In typical use, both Path and Args are set by calling Command.
Args []string
// Env specifies the environment of the process.
// Each entry is of the form "key=value".
// If Env is nil, the new process uses the current process's
// environment.
// If Env contains duplicate environment keys, only the last
// value in the slice for each duplicate key is used.
// As a special case on Windows, SYSTEMROOT is always added if
// missing and not explicitly set to the empty string.
//
// See also the Dir field, which may set PWD in the environment.
Env []string
// Dir specifies the working directory of the command.
// If Dir is the empty string, Run runs the command in the
// calling process's current directory.
//
// On Unix systems, the value of Dir also determines the
// child process's PWD environment variable if not otherwise
// specified. A Unix process represents its working directory
// not by name but as an implicit reference to a node in the
// file tree. So, if the child process obtains its working
// directory by calling a function such as C's getcwd, which
// computes the canonical name by walking up the file tree, it
// will not recover the original value of Dir if that value
// was an alias involving symbolic links. However, if the
// child process calls Go's [os.Getwd] or GNU C's
// get_current_dir_name, and the value of PWD is an alias for
// the current directory, those functions will return the
// value of PWD, which matches the value of Dir.
Dir string
// Stdin specifies the process's standard input.
//
// If Stdin is nil, the process reads from the null device (os.DevNull).
//
// If Stdin is an *os.File, the process's standard input is connected
// directly to that file.
//
// Otherwise, during the execution of the command a separate
// goroutine reads from Stdin and delivers that data to the command
// over a pipe. In this case, Wait does not complete until the goroutine
// stops copying, either because it has reached the end of Stdin
// (EOF or a read error), or because writing to the pipe returned an error,
// or because a nonzero WaitDelay was set and expired.
//
// Regardless of WaitDelay, Wait can block until a Read from
// Stdin completes. If you need to use a blocking io.Reader,
// use the StdinPipe method to get a pipe, copy from the Reader
// to the pipe, and arrange to close the Reader after Wait returns.
Stdin io.Reader
// Stdout and Stderr specify the process's standard output and error.
//
// If either is nil, Run connects the corresponding file descriptor
// to the null device (os.DevNull).
//
// If either is an *os.File, the corresponding output from the process
// is connected directly to that file.
//
// Otherwise, during the execution of the command a separate goroutine
// reads from the process over a pipe and delivers that data to the
// corresponding Writer. In this case, Wait does not complete until the
// goroutine reaches EOF or encounters an error or a nonzero WaitDelay
// expires.
//
// Regardless of WaitDelay, Wait can block until a Write to
// Stdout or Stderr completes. If you need to use a blocking io.Writer,
// use the StdoutPipe or StderrPipe method to get a pipe,
// copy from the pipe to the Writer, and arrange to close the
// Writer after Wait returns.
//
// If Stdout and Stderr are the same writer, and have a type that can
// be compared with ==, at most one goroutine at a time will call Write.
Stdout io.Writer
Stderr io.Writer
// ExtraFiles specifies additional open files to be inherited by the
// new process. It does not include standard input, standard output, or
// standard error. If non-nil, entry i becomes file descriptor 3+i.
//
// ExtraFiles is not supported on Windows.
ExtraFiles []*os.File
// SysProcAttr holds optional, operating system-specific attributes.
// Run passes it to os.StartProcess as the os.ProcAttr's Sys field.
SysProcAttr *syscall.SysProcAttr
// Process is the underlying process, once started.
Process *os.Process
// ProcessState contains information about an exited process.
// If the process was started successfully, Wait or Run will
// populate its ProcessState when the command completes.
ProcessState *os.ProcessState
Err error // LookPath error, if any; added in Go 1.19
// If Cancel is non-nil, the command must have been created with
// CommandContext and Cancel will be called when the command's
// Context is done. By default, CommandContext sets Cancel to
// call the Kill method on the command's Process.
//
// Typically a custom Cancel will send a signal to the command's
// Process, but it may instead take other actions to initiate cancellation,
// such as closing a stdin or stdout pipe or sending a shutdown request on a
// network socket.
//
// If the command exits with a success status after Cancel is
// called, and Cancel does not return an error equivalent to
// os.ErrProcessDone, then Wait and similar methods will return a non-nil
// error: either an error wrapping the one returned by Cancel,
// or the error from the Context.
// (If the command exits with a non-success status, or Cancel
// returns an error that wraps os.ErrProcessDone, Wait and similar methods
// continue to return the command's usual exit status.)
//
// If Cancel is set to nil, nothing will happen immediately when the command's
// Context is done, but a nonzero WaitDelay will still take effect. That may
// be useful, for example, to work around deadlocks in commands that do not
// support shutdown signals but are expected to always finish quickly.
//
// Cancel will not be called if Start returns a non-nil error.
Cancel func() error // Go 1.20
// If WaitDelay is non-zero, it bounds the time spent waiting on two sources
// of unexpected delay in Wait: a child process that fails to exit after the
// associated Context is canceled, and a child process that exits but leaves
// its I/O pipes unclosed.
//
// The WaitDelay timer starts when either the associated Context is done or a
// call to Wait observes that the child process has exited, whichever occurs
// first. When the delay has elapsed, the command shuts down the child process
// and/or its I/O pipes.
//
// If the child process has failed to exit — perhaps because it ignored or
// failed to receive a shutdown signal from a Cancel function, or because no
// Cancel function was set — then it will be terminated using os.Process.Kill.
//
// Then, if the I/O pipes communicating with the child process are still open,
// those pipes are closed in order to unblock any goroutines currently blocked
// on Read or Write calls.
//
// If pipes are closed due to WaitDelay, no Cancel call has occurred,
// and the command has otherwise exited with a successful status, Wait and
// similar methods will return ErrWaitDelay instead of nil.
//
// If WaitDelay is zero (the default), I/O pipes will be read until EOF,
// which might not occur until orphaned subprocesses of the command have
// also closed their descriptors for the pipes.
WaitDelay time.Duration // Go 1.20
// contains filtered or unexported fields
}
func Command
func Command(name string, arg ...string) *Cmd
Command возвращает структуру Cmd для выполнения указанной программы с заданными аргументами.
В возвращаемой структуре задаются только Path и Args.
Если name не содержит разделителей пути, Command использует LookPath, чтобы по возможности разрешить name в полный путь. В противном случае name используется напрямую как Path.
Поле Args возвращённого Cmd формируется из имени команды, за которым следуют элементы arg, поэтому arg не должен содержать само имя команды. Например, в Command("echo", "hello") значение Args[0] всегда равно name, а не, возможно, разрешённому Path.
В Windows процессы получают всю командную строку как единую строку и сами выполняют её разбор. Command объединяет Args и заключает их в кавычки, формируя командную строку с помощью алгоритма, совместимого с приложениями, использующими CommandLineToArgvW (это самый распространённый способ). Заметными исключениями являются msiexec.exe и cmd.exe (а следовательно, и все пакетные файлы), использующие другой алгоритм снятия кавычек. В этих и других подобных случаях кавычки можно расставить самостоятельно и передать полную командную строку в SysProcAttr.CmdLine, оставив Args пустым.
Пример
Код:
cmd := exec.Command("tr", "a-z", "A-Z")
cmd.Stdin = strings.NewReader("some input")
var out strings.Builder
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf("in all caps: %q\n", out.String())
Пример (окружение)
Код:
cmd := exec.Command("prog")
cmd.Env = append(os.Environ(),
"FOO=duplicate_value", // ignored
"FOO=actual_value", // this value is used
)
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
func CommandContext 1.7
func CommandContext(ctx context.Context, name string, arg ...string) *Cmd
CommandContext похожа на Command, но принимает контекст.
Переданный контекст используется для прерывания процесса (посредством вызова cmd.Cancel или os.Process.Kill), если контекст завершится до того, как команда завершится самостоятельно.
CommandContext задаёт для функции Cancel команды вызов метода Kill у её Process и оставляет WaitDelay незаданным. Вызывающий код может изменить поведение отмены, изменив эти поля до запуска команды.
Пример
Код:
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil {
// This will fail after 100 milliseconds. The 5 second sleep
// will be interrupted.
}
func (*Cmd) CombinedOutput
func (c *Cmd) CombinedOutput() ([]byte, error)
CombinedOutput запускает команду и возвращает объединённые стандартный вывод и стандартный поток ошибок.
Пример
Код:
cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", stdoutStderr)
func (*Cmd) Environ 1.19
func (c *Cmd) Environ() []string
Environ возвращает копию окружения, в котором будет запущена команда с её текущими настройками.
Пример
Код:
cmd := exec.Command("pwd")
// Set Dir before calling cmd.Environ so that it will include an
// updated PWD variable (on platforms where that is used).
cmd.Dir = ".."
cmd.Env = append(cmd.Environ(), "POSIXLY_CORRECT=1")
out, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
func (*Cmd) Output
func (c *Cmd) Output() ([]byte, error)
Output запускает команду и возвращает её стандартный вывод. Возвращённая ошибка обычно имеет тип *ExitError. Если c.Stderr равен nil и возвращённая ошибка имеет тип *ExitError, Output заполняет поле Stderr возвращённой ошибки.
Пример
Код:
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
func (*Cmd) Run
func (c *Cmd) Run() error
Run запускает указанную команду и ожидает её завершения.
Возвращённая ошибка равна nil, если команда выполняется, при копировании stdin, stdout и stderr не возникает проблем, а команда завершается с нулевым кодом выхода.
Если команда запускается, но завершается неуспешно, ошибка имеет тип *ExitError. В других ситуациях могут возвращаться ошибки других типов.
Если вызывающая горутина заблокировала поток операционной системы с помощью runtime.LockOSThread и изменила какое-либо наследуемое состояние потока на уровне ОС (например, пространства имён Linux или Plan 9), новый процесс унаследует состояние потока вызывающего кода.
Пример
Код:
cmd := exec.Command("sleep", "1")
log.Printf("Running command and waiting for it to finish...")
err := cmd.Run()
log.Printf("Command finished with error: %v", err)
func (*Cmd) Start
func (c *Cmd) Start() error
Start запускает указанную команду, но не ожидает её завершения.
Если Start возвращает успешный результат, поле c.Process будет установлено.
После успешного вызова Start необходимо вызвать метод Cmd.Wait, чтобы освободить связанные системные ресурсы.
Пример
Код:
cmd := exec.Command("sleep", "5")
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
log.Printf("Waiting for command to finish...")
err = cmd.Wait()
log.Printf("Command finished with error: %v", err)
func (*Cmd) StderrPipe
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe возвращает канал, который будет подключён к стандартному потоку ошибок команды при её запуске.
Cmd.Wait закроет канал после обнаружения завершения команды, поэтому большинству вызывающих не нужно закрывать его самостоятельно. Следовательно, вызывать Wait до завершения чтения из канала нельзя. По той же причине при использовании StderrPipe нельзя вызывать Cmd.Run. Пример идиоматичного использования см. в примере StdoutPipe.
Пример
Код:
cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
slurp, _ := io.ReadAll(stderr)
fmt.Printf("%s\n", slurp)
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
func (*Cmd) StdinPipe
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
StdinPipe возвращает канал, который будет подключён к стандартному вводу команды при её запуске. Канал автоматически закроется после того, как Cmd.Wait обнаружит завершение команды. Чтобы закрыть канал раньше, вызывающему достаточно вызвать Close. Например, если выполняемая команда не завершится, пока стандартный ввод не будет закрыт, вызывающий код должен закрыть канал.
Пример
Код:
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
go func() {
defer stdin.Close()
io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
}()
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
func (*Cmd) StdoutPipe
func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
StdoutPipe возвращает канал, который будет подключён к стандартному выводу команды при её запуске.
Cmd.Wait закроет канал после обнаружения завершения команды, поэтому большинству вызывающих не нужно закрывать его самостоятельно. Следовательно, вызывать Wait до завершения чтения из канала нельзя. По той же причине при использовании StdoutPipe нельзя вызывать Cmd.Run. Пример идиоматичного использования см. в примере.
Пример
Код:
cmd := exec.Command("echo", "-n", `{"Name": "Bob", "Age": 32}`)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
var person struct {
Name string
Age int
}
if err := json.NewDecoder(stdout).Decode(&person); err != nil {
log.Fatal(err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %d years old\n", person.Name, person.Age)
func (*Cmd) String 1.13
func (c *Cmd) String() string
String возвращает понятное человеку описание c. Оно предназначено только для отладки. В частности, его нельзя использовать в качестве входных данных для оболочки. Вывод String может различаться в разных выпусках Go.
func (*Cmd) Wait
func (c *Cmd) Wait() error
Wait ожидает завершения команды, а также окончания копирования данных во входной поток или из стандартного вывода и стандартного потока ошибок.
Команда должна быть запущена с помощью Cmd.Start.
Возвращённая ошибка равна nil, если команда выполняется, при копировании stdin, stdout и stderr не возникает проблем, а команда завершается с нулевым кодом выхода.
Если команда не запускается или завершается неуспешно, ошибка имеет тип *ExitError. При проблемах ввода-вывода могут возвращаться ошибки других типов.
Если c.Stdin, c.Stdout или c.Stderr не являются значениями типа *os.File, Wait также ожидает завершения соответствующего цикла ввода-вывода, копирующего данные в процесс или из него.
Нельзя вызывать Wait одновременно из нескольких горутин. Пользовательская функция Cmd.Cancel не должна вызывать Wait.
Wait освобождает ресурсы, связанные с Cmd.
type Error
Error возвращается функцией LookPath, если ей не удаётся определить, является ли файл исполняемым.
type Error struct {
// Name is the file name for which the error occurred.
Name string
// Err is the underlying error.
Err error
}
func (*Error) Error
func (e *Error) Error() string
func (*Error) Unwrap 1.13
func (e *Error) Unwrap() error
type ExitError
ExitError сообщает о неуспешном завершении команды.
type ExitError struct {
*os.ProcessState
// Stderr holds a subset of the standard error output from the
// Cmd.Output method if standard error was not otherwise being
// collected.
//
// If the error output is long, Stderr may contain only a prefix
// and suffix of the output, with the middle replaced with
// text about the number of omitted bytes.
//
// Stderr is provided for debugging, for inclusion in error messages.
// Users with other needs should redirect Cmd.Stderr as needed.
Stderr []byte // Go 1.6
}
func (*ExitError) Error
func (e *ExitError) Error() string
Подкаталоги
| Имя | Описание |
|---|---|
| .. | |
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/os/exec/