script

package
v1.21.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 6, 2024 License: BSD-3-Clause Imports: 22 Imported by: 0

Documentation

Overview

Package script implements a small, customizable, platform-agnostic scripting language.

Scripts are run by an Engine configured with a set of available commands and conditions that guard those commands. Each script has an associated working directory and environment, along with a buffer containing the stdout and stderr output of a prior command, tracked in a State that commands can inspect and modify.

The default commands configured by NewEngine resemble a simplified Unix shell.

Script Language

Each line of a script is parsed into a sequence of space-separated command words, with environment variable expansion within each word and # marking an end-of-line comment. Additional variables named ':' and '/' are expanded within script arguments (expanding to the value of os.PathListSeparator and os.PathSeparator respectively) but are not inherited in subprocess environments.

Adding single quotes around text keeps spaces in that text from being treated as word separators and also disables environment variable expansion. Inside a single-quoted block of text, a repeated single quote indicates a literal single quote, as in:

'Don''t communicate by sharing memory.'

A line beginning with # is a comment and conventionally explains what is being done or tested at the start of a new section of the script.

Commands are executed one at a time, and errors are checked for each command; if any command fails unexpectedly, no subsequent commands in the script are executed. The command prefix ! indicates that the command on the rest of the line (typically go or a matching predicate) must fail instead of succeeding. The command prefix ? indicates that the command may or may not succeed, but the script should continue regardless.

The command prefix [cond] indicates that the command on the rest of the line should only run when the condition is satisfied.

A condition can be negated: [!root] means to run the rest of the line only if the user is not root. Multiple conditions may be given for a single command, for example, '[linux] [amd64] skip'. The command will run if all conditions are satisfied.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnexpectedSuccess = errors.New("unexpected success")

ErrUnexpectedSuccess indicates that a script command that was expected to fail (as indicated by a "!" prefix) instead completed successfully.

View Source
var ErrUsage = errors.New("invalid usage")

ErrUsage may be returned by a Command to indicate that it was called with invalid arguments; its Usage method may be called to obtain details.

Functions

func DefaultCmds

func DefaultCmds() map[string]Cmd

DefaultCmds returns a set of broadly useful script commands.

Run the 'help' command within a script engine to view a list of the available commands.

func DefaultConds

func DefaultConds() map[string]Cond

DefaultConds returns a set of broadly useful script conditions.

Run the 'help' command within a script engine to view a list of the available conditions.

Types

type Cmd

type Cmd interface {
	// Run begins running the command.
	//
	// If the command produces output or can be run in the background, run returns
	// a WaitFunc that will be called to obtain the result of the command and
	// update the engine's stdout and stderr buffers.
	//
	// Run itself and the returned WaitFunc may inspect and/or modify the State,
	// but the State's methods must not be called concurrently after Run has
	// returned.
	//
	// Run may retain and access the args slice until the WaitFunc has returned.
	Run(s *State, args ...string) (WaitFunc, error)

	// Usage returns the usage for the command, which the caller must not modify.
	Usage() *CmdUsage
}

A Cmd is a command that is available to a script.

func Cat

func Cat() Cmd

Cat writes the concatenated contents of the named file(s) to the script's stdout buffer.

func Cd

func Cd() Cmd

Cd changes the current working directory.

func Chmod

func Chmod() Cmd

Chmod changes the permissions of a file or a directory..

func Cmp

func Cmp() Cmd

Cmp compares the contents of two files, or the contents of either the "stdout" or "stderr" buffer and a file, returning a non-nil error if the contents differ.

func Cmpenv

func Cmpenv() Cmd

Cmpenv is like Compare, but also performs environment substitutions on the contents of both arguments.

func Command

func Command(usage CmdUsage, run func(*State, ...string) (WaitFunc, error)) Cmd

Command returns a new Cmd with a Usage method that returns a copy of the given CmdUsage and a Run method calls the given function.

func Cp

func Cp() Cmd

Cp copies one or more files to a new location.

func Echo

func Echo() Cmd

Echo writes its arguments to stdout, followed by a newline.

func Env

func Env() Cmd

Env sets or logs the values of environment variables.

With no arguments, Env reports all variables in the environment. "key=value" arguments set variables, and arguments without "=" cause the corresponding value to be printed to the stdout buffer.

func Exec

func Exec(cancel func(*exec.Cmd) error, waitDelay time.Duration) Cmd

Exec runs an arbitrary executable as a subprocess.

When the Script's context is canceled, Exec sends the interrupt signal, then waits for up to the given delay for the subprocess to flush output before terminating it with os.Kill.

func Exists

func Exists() Cmd

Exists checks that the named file(s) exist.

func Grep

func Grep() Cmd

Grep checks that file content matches a regexp. Like stdout/stderr and unlike Unix grep, it accepts Go regexp syntax.

Grep does not modify the State's stdout or stderr buffers. (Its output goes to the script log, not stdout.)

func Help

func Help() Cmd

Help writes command documentation to the script log.

func Mkdir

func Mkdir() Cmd

Mkdir creates a directory and any needed parent directories.

func Mv

func Mv() Cmd

Mv renames an existing file or directory to a new path.

func Program

func Program(name string, cancel func(*exec.Cmd) error, waitDelay time.Duration) Cmd

Program returns a new command that runs the named program, found from the host process's PATH (not looked up in the script's PATH).

func Replace

func Replace() Cmd

Replace replaces all occurrences of a string in a file with another string.

func Rm

func Rm() Cmd

Rm removes a file or directory.

If a directory, Rm also recursively removes that directory's contents.

func Sleep

func Sleep() Cmd

Sleep sleeps for the given Go duration or until the script's context is cancelled, whichever happens first.

func Stderr

func Stderr() Cmd

Stderr searches for a regular expression in the stderr buffer.

func Stdout

func Stdout() Cmd

Stdout searches for a regular expression in the stdout buffer.

func Stop

func Stop() Cmd

Stop returns a sentinel error that causes script execution to halt and s.Execute to return with a nil error.

func Symlink() Cmd

Symlink creates a symbolic link.

func Wait

func Wait() Cmd

Wait waits for the completion of background commands.

When Wait returns, the stdout and stderr buffers contain the concatenation of the background commands' respective outputs in the order in which those commands were started.

type CmdUsage

type CmdUsage struct {
	Summary string   // in the style of the Name section of a Unix 'man' page, omitting the name
	Args    string   // a brief synopsis of the command's arguments (only)
	Detail  []string // zero or more sentences in the style of the Description section of a Unix 'man' page

	// If Async is true, the Cmd is meaningful to run in the background, and its
	// Run method must return either a non-nil WaitFunc or a non-nil error.
	Async bool

	// RegexpArgs reports which arguments, if any, should be treated as regular
	// expressions. It takes as input the raw, unexpanded arguments and returns
	// the list of argument indices that will be interpreted as regular
	// expressions.
	//
	// If RegexpArgs is nil, all arguments are assumed not to be regular
	// expressions.
	RegexpArgs func(rawArgs ...string) []int
}

A CmdUsage describes the usage of a Cmd, independent of its name (which can change based on its registration).

type CommandError

type CommandError struct {
	File string
	Line int
	Op   string
	Args []string
	Err  error
}

A CommandError describes an error resulting from attempting to execute a specific command.

func (*CommandError) Error

func (e *CommandError) Error() string

func (*CommandError) Unwrap

func (e *CommandError) Unwrap() error

type Cond

type Cond interface {
	// Eval reports whether the condition applies to the given State.
	//
	// If the condition's usage reports that it is a prefix,
	// the condition must be used with a suffix.
	// Otherwise, the passed-in suffix argument is always the empty string.
	Eval(s *State, suffix string) (bool, error)

	// Usage returns the usage for the condition, which the caller must not modify.
	Usage() *CondUsage
}

A Cond is a condition deciding whether a command should be run.

func BoolCondition

func BoolCondition(summary string, v bool) Cond

BoolCondition returns a Cond with the given truth value and summary. The Cond rejects the use of condition suffixes.

func CachedCondition

func CachedCondition(summary string, eval func(string) (bool, error)) Cond

CachedCondition is like Condition but only calls eval the first time the condition is evaluated for a given suffix. Future calls with the same suffix reuse the earlier result.

The eval function is not passed a *State because the condition is cached across all execution states and must not vary by state.

func Condition

func Condition(summary string, eval func(*State) (bool, error)) Cond

Condition returns a Cond with the given summary and evaluation function.

func OnceCondition

func OnceCondition(summary string, eval func() (bool, error)) Cond

OnceCondition returns a Cond that calls eval the first time the condition is evaluated. Future calls reuse the same result.

The eval function is not passed a *State because the condition is cached across all execution states and must not vary by state.

func PrefixCondition

func PrefixCondition(summary string, eval func(*State, string) (bool, error)) Cond

PrefixCondition returns a Cond with the given summary and evaluation function.

type CondUsage

type CondUsage struct {
	Summary string // a single-line summary of when the condition is true

	// If Prefix is true, the condition is a prefix and requires a
	// colon-separated suffix (like "[GOOS:linux]" for the "GOOS" condition).
	// The suffix may be the empty string (like "[prefix:]").
	Prefix bool
}

A CondUsage describes the usage of a Cond, independent of its name (which can change based on its registration).

type Engine

type Engine struct {
	Cmds  map[string]Cmd
	Conds map[string]Cond

	// If Quiet is true, Execute deletes log prints from the previous
	// section when starting a new section.
	Quiet bool
}

An Engine stores the configuration for executing a set of scripts.

The same Engine may execute multiple scripts concurrently.

func NewEngine

func NewEngine() *Engine

NewEngine returns an Engine configured with a basic set of commands and conditions.

func (*Engine) Execute

func (e *Engine) Execute(s *State, file string, script *bufio.Reader, log io.Writer) (err error)

Execute reads and executes script, writing the output to log.

Execute stops and returns an error at the first command that does not succeed. The returned error's text begins with "file:line: ".

If the script runs to completion or ends by a 'stop' command, Execute returns nil.

Execute does not stop background commands started by the script before returning. To stop those, use State.CloseAndWait or the Wait command.

func (*Engine) ListCmds

func (e *Engine) ListCmds(w io.Writer, verbose bool, names ...string) error

ListCmds prints to w a list of the named commands, annotating each with its arguments and a short usage summary. If verbose is true, ListCmds prints full details for each command.

Each of the name arguments should be a command name. If no names are passed as arguments, ListCmds lists all the commands registered in e.

func (*Engine) ListConds

func (e *Engine) ListConds(w io.Writer, s *State, tags ...string) error

ListConds prints to w a list of conditions, one per line, annotating each with a description and whether the condition is true in the state s (if s is non-nil).

Each of the tag arguments should be a condition string of the form "name" or "name:suffix". If no tags are passed as arguments, ListConds lists all conditions registered in the engine e.

type State

type State struct {
	// contains filtered or unexported fields
}

A State encapsulates the current state of a running script engine, including the script environment and any running background commands.

func NewState

func NewState(ctx context.Context, workdir string, initialEnv []string) (*State, error)

NewState returns a new State permanently associated with ctx, with its initial working directory in workdir and its initial environment set to initialEnv (or os.Environ(), if initialEnv is nil).

The new State also contains pseudo-environment-variables for ${/} and ${:} (for the platform's path and list separators respectively), but does not pass those to subprocesses.

func (*State) Chdir

func (s *State) Chdir(path string) error

Chdir changes the State's working directory to the given path.

func (*State) CloseAndWait

func (s *State) CloseAndWait(log io.Writer) error

CloseAndWait cancels the State's Context and waits for any background commands to finish. If any remaining background command ended in an unexpected state, Close returns a non-nil error.

func (*State) Context

func (s *State) Context() context.Context

Context returns the Context with which the State was created.

func (*State) Environ

func (s *State) Environ() []string

Environ returns a copy of the current script environment, in the form "key=value".

func (*State) ExpandEnv

func (s *State) ExpandEnv(str string, inRegexp bool) string

ExpandEnv replaces ${var} or $var in the string according to the values of the environment variables in s. References to undefined variables are replaced by the empty string.

func (*State) ExtractFiles

func (s *State) ExtractFiles(ar *txtar.Archive) error

ExtractFiles extracts the files in ar to the state's current directory, expanding any environment variables within each name.

The files must reside within the working directory with which the State was originally created.

func (*State) Getwd

func (s *State) Getwd() string

Getwd returns the directory in which to run the next script command.

func (*State) Logf

func (s *State) Logf(format string, args ...any)

Logf writes output to the script's log without updating its stdout or stderr buffers. (The output log functions as a kind of meta-stderr.)

func (*State) LookupEnv

func (s *State) LookupEnv(key string) (string, bool)

LookupEnv retrieves the value of the environment variable in s named by the key.

func (*State) Path

func (s *State) Path(path string) string

Path returns the absolute path in the host operating system for a script-based (generally slash-separated and relative) path.

func (*State) Setenv

func (s *State) Setenv(key, value string) error

Setenv sets the value of the environment variable in s named by the key.

func (*State) Stderr

func (s *State) Stderr() string

Stderr returns the stderr output of the last command run, or the empty string if no command has been run.

func (*State) Stdout

func (s *State) Stdout() string

Stdout returns the stdout output of the last command run, or the empty string if no command has been run.

type UsageError

type UsageError struct {
	Name    string
	Command Cmd
}

A UsageError reports the valid arguments for a command.

It may be returned in response to invalid arguments.

func (*UsageError) Error

func (e *UsageError) Error() string

type WaitFunc

type WaitFunc func(*State) (stdout, stderr string, err error)

A WaitFunc is a function called to retrieve the results of a Cmd.

Directories

Path Synopsis
Package scripttest adapts the script engine for use in tests.
Package scripttest adapts the script engine for use in tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL